Skip to content

Commit

Permalink
Merge pull request #326 from e-picsa/feat/dashboard-translation-import
Browse files Browse the repository at this point in the history
Feat: dashboard translation import
  • Loading branch information
chrismclarke authored Aug 19, 2024
2 parents a99778d + 5d7199d commit 100c29f
Show file tree
Hide file tree
Showing 15 changed files with 1,630 additions and 1,038 deletions.
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 { }
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();
});
});
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;
}
}
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>
}
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();
});
});
Loading

0 comments on commit 100c29f

Please sign in to comment.