feat(i18n): update database schema and domain service for translations
This commit is contained in:
@@ -3,6 +3,7 @@ export type ContentTranslationEntityType =
|
|||||||
| "branding"
|
| "branding"
|
||||||
| "calendar_event"
|
| "calendar_event"
|
||||||
| "client"
|
| "client"
|
||||||
|
| "client_activity"
|
||||||
| "planning_section"
|
| "planning_section"
|
||||||
| "project"
|
| "project"
|
||||||
| "task";
|
| "task";
|
||||||
@@ -43,6 +44,10 @@ export const contentTranslationRegistry = {
|
|||||||
client: [
|
client: [
|
||||||
{ name: "notes", label: "Notlar", kind: "textarea", maxLength: 10_000 },
|
{ name: "notes", label: "Notlar", kind: "textarea", maxLength: 10_000 },
|
||||||
],
|
],
|
||||||
|
client_activity: [
|
||||||
|
{ name: "title", label: "Başlık", required: true, maxLength: 500 },
|
||||||
|
{ name: "content", label: "İçerik", kind: "textarea", maxLength: 20_000 },
|
||||||
|
],
|
||||||
} satisfies Record<ContentTranslationEntityType, ContentTranslationField[]>;
|
} satisfies Record<ContentTranslationEntityType, ContentTranslationField[]>;
|
||||||
|
|
||||||
export function contentInputName(locale: string, field: string) {
|
export function contentInputName(locale: string, field: string) {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_content_translations` (
|
||||||
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`entity_type` text NOT NULL,
|
||||||
|
`entity_id` text NOT NULL,
|
||||||
|
`field` text NOT NULL,
|
||||||
|
`locale` text NOT NULL,
|
||||||
|
`value` text NOT NULL,
|
||||||
|
`created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,
|
||||||
|
`updated_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL,
|
||||||
|
CONSTRAINT "content_translations_entity_type_check" CHECK("__new_content_translations"."entity_type" in ('branding', 'calendar_event', 'client', 'client_activity', 'planning_section', 'project', 'task')),
|
||||||
|
CONSTRAINT "content_translations_field_check" CHECK(length("__new_content_translations"."field") between 1 and 64),
|
||||||
|
CONSTRAINT "content_translations_locale_check" CHECK(length("__new_content_translations"."locale") between 2 and 12)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_content_translations`("id", "entity_type", "entity_id", "field", "locale", "value", "created_at", "updated_at") SELECT "id", "entity_type", "entity_id", "field", "locale", "value", "created_at", "updated_at" FROM `content_translations`;--> statement-breakpoint
|
||||||
|
DROP TABLE `content_translations`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_content_translations` RENAME TO `content_translations`;--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||||
|
CREATE UNIQUE INDEX `content_translations_entity_field_locale_unique` ON `content_translations` (`entity_type`,`entity_id`,`field`,`locale`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `content_translations_locale_idx` ON `content_translations` (`locale`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `content_translations_entity_locale_idx` ON `content_translations` (`entity_type`,`entity_id`,`locale`);
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,13 @@
|
|||||||
"when": 1784413854972,
|
"when": 1784413854972,
|
||||||
"tag": "0008_wild_mercury",
|
"tag": "0008_wild_mercury",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 9,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1784533970134,
|
||||||
|
"tag": "0009_loose_sersi",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@ export type TranslationEntityType =
|
|||||||
| "branding"
|
| "branding"
|
||||||
| "calendar_event"
|
| "calendar_event"
|
||||||
| "client"
|
| "client"
|
||||||
|
| "client_activity"
|
||||||
| "planning_section"
|
| "planning_section"
|
||||||
| "project"
|
| "project"
|
||||||
| "task";
|
| "task";
|
||||||
@@ -114,7 +115,7 @@ export const contentTranslations = sqliteTable(
|
|||||||
index("content_translations_entity_locale_idx").on(table.entityType, table.entityId, table.locale),
|
index("content_translations_entity_locale_idx").on(table.entityType, table.entityId, table.locale),
|
||||||
check(
|
check(
|
||||||
"content_translations_entity_type_check",
|
"content_translations_entity_type_check",
|
||||||
sql`${table.entityType} in ('branding', 'calendar_event', 'client', 'planning_section', 'project', 'task')`,
|
sql`${table.entityType} in ('branding', 'calendar_event', 'client', 'client_activity', 'planning_section', 'project', 'task')`,
|
||||||
),
|
),
|
||||||
check("content_translations_field_check", sql`length(${table.field}) between 1 and 64`),
|
check("content_translations_field_check", sql`length(${table.field}) between 1 and 64`),
|
||||||
check("content_translations_locale_check", sql`length(${table.locale}) between 2 and 12`),
|
check("content_translations_locale_check", sql`length(${table.locale}) between 2 and 12`),
|
||||||
|
|||||||
+51
-12
@@ -69,25 +69,48 @@ export class DomainService {
|
|||||||
|
|
||||||
createClient(actor: DomainActor, input: unknown) {
|
createClient(actor: DomainActor, input: unknown) {
|
||||||
const scope = requireOwnerScope(actor);
|
const scope = requireOwnerScope(actor);
|
||||||
const value = parseDomainInput(clientCreateSchema, input);
|
const translations = this.getContentTranslations(input);
|
||||||
return this.repositories.clients.create(scope, { ...value, id: value.id ?? this.id() });
|
const defaultLocale = this.contentTranslations.getLocalizationContext(actor).defaultLocale;
|
||||||
|
const value = parseDomainInput(
|
||||||
|
clientCreateSchema,
|
||||||
|
translations ? projectBaseFromTranslations("client", input as Record<string, unknown>, translations, defaultLocale) : input,
|
||||||
|
);
|
||||||
|
const client = this.repositories.clients.create(scope, { ...value, id: value.id ?? this.id() });
|
||||||
|
this.contentTranslations.upsertEntityTranslations("client", client.id, translations);
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateClient(actor: DomainActor, id: string, input: unknown) {
|
updateClient(actor: DomainActor, id: string, input: unknown) {
|
||||||
const scope = requireOwnerScope(actor);
|
const scope = requireOwnerScope(actor);
|
||||||
const value = parseDomainInput(clientUpdateSchema, input);
|
const translations = this.getContentTranslations(input);
|
||||||
return this.repositories.clients.update(scope, id, value) ?? this.throwNotFound("Müşteri");
|
const defaultLocale = this.contentTranslations.getLocalizationContext(actor).defaultLocale;
|
||||||
|
const value = parseDomainInput(
|
||||||
|
clientUpdateSchema,
|
||||||
|
translations ? projectBaseFromTranslations("client", input as Record<string, unknown>, translations, defaultLocale) : input,
|
||||||
|
);
|
||||||
|
const client = this.repositories.clients.update(scope, id, value) ?? this.throwNotFound("Müşteri");
|
||||||
|
this.contentTranslations.upsertEntityTranslations("client", id, translations);
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteClient(actor: DomainActor, id: string) {
|
deleteClient(actor: DomainActor, id: string) {
|
||||||
return this.repositories.clients.remove(requireOwnerScope(actor), id) ?? this.throwNotFound("Müşteri");
|
const client = this.repositories.clients.remove(requireOwnerScope(actor), id) ?? this.throwNotFound("Müşteri");
|
||||||
|
this.contentTranslations.deleteEntityTranslations("client", id);
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
addClientActivity(actor: DomainActor, input: unknown) {
|
addClientActivity(actor: DomainActor, input: unknown) {
|
||||||
const scope = requireOwnerScope(actor);
|
const scope = requireOwnerScope(actor);
|
||||||
const value = parseDomainInput(clientActivityCreateSchema, input);
|
const translations = this.getContentTranslations(input);
|
||||||
|
const defaultLocale = this.contentTranslations.getLocalizationContext(actor).defaultLocale;
|
||||||
|
const value = parseDomainInput(
|
||||||
|
clientActivityCreateSchema,
|
||||||
|
translations ? projectBaseFromTranslations("client_activity", input as Record<string, unknown>, translations, defaultLocale) : input,
|
||||||
|
);
|
||||||
this.requireOwnedClient(scope, value.clientId);
|
this.requireOwnedClient(scope, value.clientId);
|
||||||
return this.repositories.clients.createActivity(scope, { ...value, id: value.id ?? this.id() });
|
const activity = this.repositories.clients.createActivity(scope, { ...value, id: value.id ?? this.id() });
|
||||||
|
this.contentTranslations.upsertEntityTranslations("client_activity", activity.id, translations);
|
||||||
|
return activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
listClientActivities(actor: DomainActor, clientId: string) {
|
listClientActivities(actor: DomainActor, clientId: string) {
|
||||||
@@ -217,9 +240,16 @@ export class DomainService {
|
|||||||
|
|
||||||
createCalendarEvent(actor: DomainActor, input: unknown) {
|
createCalendarEvent(actor: DomainActor, input: unknown) {
|
||||||
const scope = requireOwnerScope(actor);
|
const scope = requireOwnerScope(actor);
|
||||||
const value = parseDomainInput(calendarEventCreateSchema, input);
|
const translations = this.getContentTranslations(input);
|
||||||
|
const defaultLocale = this.contentTranslations.getLocalizationContext(actor).defaultLocale;
|
||||||
|
const value = parseDomainInput(
|
||||||
|
calendarEventCreateSchema,
|
||||||
|
translations ? projectBaseFromTranslations("calendar_event", input as Record<string, unknown>, translations, defaultLocale) : input,
|
||||||
|
);
|
||||||
this.assertTaskRelations(scope, value);
|
this.assertTaskRelations(scope, value);
|
||||||
return this.repositories.calendar.create(scope, { ...value, id: value.id ?? this.id() });
|
const event = this.repositories.calendar.create(scope, { ...value, id: value.id ?? this.id() });
|
||||||
|
this.contentTranslations.upsertEntityTranslations("calendar_event", event.id, translations);
|
||||||
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
listCalendarEvents(actor: DomainActor) {
|
listCalendarEvents(actor: DomainActor) {
|
||||||
@@ -229,17 +259,26 @@ export class DomainService {
|
|||||||
updateCalendarEvent(actor: DomainActor, eventId: string, input: unknown) {
|
updateCalendarEvent(actor: DomainActor, eventId: string, input: unknown) {
|
||||||
const scope = requireOwnerScope(actor);
|
const scope = requireOwnerScope(actor);
|
||||||
const current = this.repositories.calendar.get(scope, eventId) ?? this.throwNotFound("Takvim kaydı");
|
const current = this.repositories.calendar.get(scope, eventId) ?? this.throwNotFound("Takvim kaydı");
|
||||||
const value = parseDomainInput(calendarEventUpdateSchema, input);
|
const translations = this.getContentTranslations(input);
|
||||||
|
const defaultLocale = this.contentTranslations.getLocalizationContext(actor).defaultLocale;
|
||||||
|
const value = parseDomainInput(
|
||||||
|
calendarEventUpdateSchema,
|
||||||
|
translations ? projectBaseFromTranslations("calendar_event", input as Record<string, unknown>, translations, defaultLocale) : input,
|
||||||
|
);
|
||||||
const merged = { ...current, ...value };
|
const merged = { ...current, ...value };
|
||||||
if (merged.endsAt && merged.endsAt < merged.startsAt) {
|
if (merged.endsAt && merged.endsAt < merged.startsAt) {
|
||||||
throw new DomainError("VALIDATION_ERROR", "Bitiş zamanı başlangıç zamanından önce olamaz.");
|
throw new DomainError("VALIDATION_ERROR", "Bitiş zamanı başlangıç zamanından önce olamaz.");
|
||||||
}
|
}
|
||||||
this.assertTaskRelations(scope, merged);
|
this.assertTaskRelations(scope, merged);
|
||||||
return this.repositories.calendar.update(scope, eventId, value) ?? this.throwNotFound("Takvim kaydı");
|
const event = this.repositories.calendar.update(scope, eventId, value) ?? this.throwNotFound("Takvim kaydı");
|
||||||
|
this.contentTranslations.upsertEntityTranslations("calendar_event", eventId, translations);
|
||||||
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteCalendarEvent(actor: DomainActor, eventId: string) {
|
deleteCalendarEvent(actor: DomainActor, eventId: string) {
|
||||||
return this.repositories.calendar.remove(requireOwnerScope(actor), eventId) ?? this.throwNotFound("Takvim kaydı");
|
const event = this.repositories.calendar.remove(requireOwnerScope(actor), eventId) ?? this.throwNotFound("Takvim kaydı");
|
||||||
|
this.contentTranslations.deleteEntityTranslations("calendar_event", eventId);
|
||||||
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
createFinanceTransaction(actor: DomainActor, input: unknown) {
|
createFinanceTransaction(actor: DomainActor, input: unknown) {
|
||||||
|
|||||||
Reference in New Issue
Block a user