-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #326 from e-picsa/feat/dashboard-translation-import
Feat: dashboard translation import
- Loading branch information
Showing
15 changed files
with
1,630 additions
and
1,038 deletions.
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
...src/app/modules/translations/pages/import/components/csv-import/csv-import.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<p>Import a <code>[language_code].csv</code> file to add translated strings</p> | ||
<div style="display: flex; align-items: center"> | ||
<button | ||
mat-raised-button | ||
color="accent" | ||
style="margin-left: auto; color: white" | ||
[disabled]="importTotal() <= 0 || importCounter() > 0" | ||
(click)="processImport()" | ||
> | ||
<span>Import</span> | ||
@if(importTotal()>0){ | ||
<span style="margin-left: 8px">{{ importCounter() }} / {{ importTotal() }}</span> | ||
} | ||
</button> | ||
</div> | ||
|
||
@if (importTotal()===0) { | ||
<div #dragDrop></div> | ||
} @else { } |
Empty file.
22 changes: 22 additions & 0 deletions
22
.../app/modules/translations/pages/import/components/csv-import/csv-import.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { TranslationsCSVImportComponent } from './csv-import.component'; | ||
|
||
describe('CsvImportComponent', () => { | ||
let component: TranslationsCSVImportComponent; | ||
let fixture: ComponentFixture<TranslationsCSVImportComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [TranslationsCSVImportComponent], | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(TranslationsCSVImportComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
110 changes: 110 additions & 0 deletions
110
...d/src/app/modules/translations/pages/import/components/csv-import/csv-import.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* eslint-disable @nx/enforce-module-boundaries */ | ||
import { CommonModule } from '@angular/common'; | ||
import { Component, computed, effect, ElementRef, signal, viewChild } from '@angular/core'; | ||
import { MatButtonModule } from '@angular/material/button'; | ||
import { ILocaleCode, LOCALES_DATA_HASHMAP } from '@picsa/data'; | ||
import type { Database } from '@picsa/server-types'; | ||
import { arrayToHashmap, loadCSV } from '@picsa/utils'; | ||
import Uppy from '@uppy/core'; | ||
import DragDrop from '@uppy/drag-drop'; | ||
|
||
import { TranslationDashboardService } from '../../../../translations.service'; | ||
|
||
type ITranslationRow = Database['public']['Tables']['translations']['Row']; | ||
|
||
enum Action { | ||
skip = 'skip', | ||
add = 'add', | ||
update = 'update', | ||
} | ||
type ActionSummary = { [key in Action]: ITranslationRow[] }; | ||
|
||
@Component({ | ||
selector: 'dashboard-translation-csv-import', | ||
standalone: true, | ||
imports: [CommonModule, MatButtonModule], | ||
templateUrl: './csv-import.component.html', | ||
styleUrl: './csv-import.component.scss', | ||
}) | ||
export class TranslationsCSVImportComponent { | ||
public importSummary = signal<ActionSummary>(this.prepareActions([], [])); | ||
public importTotal = computed(() => { | ||
const { add, update } = this.importSummary(); | ||
return add.length + update.length; | ||
}); | ||
|
||
public importCounter = signal(0); | ||
|
||
private uppy: Uppy; | ||
private dropEl = viewChild('dragDrop', { read: ElementRef }); | ||
|
||
constructor(private service: TranslationDashboardService) { | ||
effect( | ||
() => { | ||
const el = this.dropEl(); | ||
if (el) { | ||
this.setupDropZone(el.nativeElement); | ||
// eagerly initialise service in case not previously (ensured during process) | ||
this.service.ready(); | ||
} | ||
}, | ||
{ allowSignalWrites: true } | ||
); | ||
} | ||
|
||
public async processImport() { | ||
const { add, update } = this.importSummary(); | ||
// TODO - check if working as expected | ||
for (const entry of add) { | ||
await this.service.addTranslation(entry); | ||
this.importCounter.update((v) => v + 1); | ||
} | ||
for (const entry of update) { | ||
await this.service.updateTranslationById(entry.id, entry); | ||
this.importCounter.update((v) => v + 1); | ||
} | ||
} | ||
|
||
private setupDropZone(target: ElementRef<HTMLDivElement>) { | ||
if (this.uppy) return; | ||
this.uppy = new Uppy({ restrictions: { allowedFileTypes: ['.csv'] } }).use(DragDrop, { | ||
target: target as any, | ||
inputName: 'translations', | ||
replaceTargetContent: true, | ||
id: 'translations', | ||
allowMultipleFiles: false, | ||
note: 'Accepts CSV Translations list', | ||
}); | ||
this.uppy.on('file-added', async ({ data }) => { | ||
const text = await data.text(); | ||
const rows = await loadCSV<ITranslationRow>(text, { header: true, skipEmptyLines: true }); | ||
// TODO - QC check | ||
const locales = Object.keys(rows[0]).filter((key) => key in LOCALES_DATA_HASHMAP) as ILocaleCode[]; | ||
const actions = this.prepareActions(rows, locales); | ||
console.log({ actions }); | ||
this.importSummary.set(actions); | ||
}); | ||
} | ||
|
||
/** | ||
* Check the dropped file to see if it contains translation entries and review | ||
* what database actions are required for each when comparing to values | ||
* that already exist on server (i.e. update, skip, archive, restore) | ||
*/ | ||
private prepareActions(rows: ITranslationRow[], locales: ILocaleCode[]) { | ||
const serverHashmap = arrayToHashmap(this.service.translations(), 'id'); | ||
const actions: ActionSummary = { update: [], add: [], skip: [] }; | ||
for (const row of rows) { | ||
// NOTE - csv does not contain row by default | ||
row.id = this.service.generateTranslationID(row); | ||
if (row.id in serverHashmap) { | ||
// TODO - detect which locales updated (for now assume all) | ||
const updated = { ...serverHashmap[row.id], ...row }; | ||
actions.update.push(updated); | ||
} else { | ||
actions.add.push(row); | ||
} | ||
} | ||
return actions; | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
...c/app/modules/translations/pages/import/components/json-import/json-import.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<p>Import a <code>template.json</code> file to add new strings for translation</p> | ||
<div style="display: flex; align-items: center"> | ||
<button | ||
mat-raised-button | ||
color="accent" | ||
style="margin-left: auto; color: white" | ||
[disabled]="importTotal() <= 0 || importCounter() > 0" | ||
(click)="processImport()" | ||
> | ||
<span>Import</span> | ||
@if(importTotal()>0){ | ||
<span style="margin-left: 8px">{{ importCounter() }} / {{ importTotal() }}</span> | ||
} | ||
</button> | ||
</div> | ||
|
||
@if (importTotal()===-1) { | ||
<div #dragDrop></div> | ||
} @else { | ||
<mat-tab-group mat-stretch-tabs="false" mat-align-tabs="start"> | ||
@for(table of summaryTables(); track table.key){ | ||
<mat-tab [label]="table.label"> | ||
<picsa-data-table [options]="tableOptions" [data]="table.data"></picsa-data-table> | ||
</mat-tab> | ||
} | ||
</mat-tab-group> | ||
} |
Empty file.
22 changes: 22 additions & 0 deletions
22
...pp/modules/translations/pages/import/components/json-import/json-import.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { JsonImportComponent } from './json-import.component'; | ||
|
||
describe('JsonImportComponent', () => { | ||
let component: JsonImportComponent; | ||
let fixture: ComponentFixture<JsonImportComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [JsonImportComponent], | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(JsonImportComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
Oops, something went wrong.