113988273f
Software catalog with per-customer assignments via the customer_software
join table. Two tabs in one /software page:
Catalog tab:
- Software CRUD: name, version, description, defaultFee (TRY).
- Deleting a software cascades and removes all its assignments first
(best-effort loop, then the catalog row), all wrapped in audit logs.
Assignments tab:
- M2M between customer and software with own fee (overrides defaultFee),
billingPeriod (monthly default), startDate/endDate, notes.
- Form auto-fills fee from selected software's defaultFee.
- Both Sheet forms localized; date inputs round-tripped via toIsoDate
(Appwrite expects ISO 8601 with TZ; HTML date input gives YYYY-MM-DD).
- Delete dialogs differentiated for catalog ('siliniyor') vs assignment
('kaldırılıyor').
New files:
- lib/validation/software.ts (softwareSchema + customerSoftwareSchema)
- lib/appwrite/software-actions.ts (6 server actions)
- lib/appwrite/software-queries.ts (listSoftware, listAssignments)
- lib/appwrite/software-types.ts (form state)
- /software route with SoftwareClient (Tabs), SoftwareFormSheet,
AssignmentFormSheet, inline delete dialogs.
Empty states surface the right next-step CTA: 'önce müşteri ekleyin', or
'önce yazılım ekleyin', as appropriate.
48 lines
1.1 KiB
TypeScript
48 lines
1.1 KiB
TypeScript
import "server-only";
|
|
|
|
import { Query } from "node-appwrite";
|
|
|
|
import { createAdminClient } from "./server";
|
|
import {
|
|
DATABASE_ID,
|
|
TABLES,
|
|
type CustomerSoftware,
|
|
type Software,
|
|
} from "./schema";
|
|
|
|
export async function listSoftware(tenantId: string): Promise<Software[]> {
|
|
try {
|
|
const { tablesDB } = createAdminClient();
|
|
const result = await tablesDB.listRows({
|
|
databaseId: DATABASE_ID,
|
|
tableId: TABLES.software,
|
|
queries: [
|
|
Query.equal("tenantId", tenantId),
|
|
Query.orderAsc("name"),
|
|
Query.limit(500),
|
|
],
|
|
});
|
|
return result.rows as unknown as Software[];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function listAssignments(tenantId: string): Promise<CustomerSoftware[]> {
|
|
try {
|
|
const { tablesDB } = createAdminClient();
|
|
const result = await tablesDB.listRows({
|
|
databaseId: DATABASE_ID,
|
|
tableId: TABLES.customerSoftware,
|
|
queries: [
|
|
Query.equal("tenantId", tenantId),
|
|
Query.orderDesc("$createdAt"),
|
|
Query.limit(1000),
|
|
],
|
|
});
|
|
return result.rows as unknown as CustomerSoftware[];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|