+
+
+ {m["components.slotTemplate.template"]({ no: index + 1 })}
+
+
+
+
+
+ {#snippet children({ props })}
+
+ {/snippet}
+
+
+
+
+ {#snippet children({ props })}
+ {m["components.slotTemplate.weekdays"]()}
+
+
+ {toWeekdaysLabel(value.weekdays)}
+
+
+ {weekdays[0].day}
+ {weekdays[1].day}
+ {weekdays[2].day}
+ {weekdays[3].day}
+ {weekdays[4].day}
+ {weekdays[5].day}
+ {weekdays[6].day}
+
+
+ {/snippet}
+
+
+
+
+
+
+ {#snippet children({ props })}
+ {m["components.slotTemplate.from"]()}
+ (value = { ...value, from: v })}
+ >
+
+ {getSelectedTime(value.from as keyof typeof times)}
+
+
+ {#each Object.entries(times) as [time, value] (time)}
+ {value.label}
+ {/each}
+
+
+ {/snippet}
+
+
+
+
+
+ {#snippet children({ props })}
+ {m["components.slotTemplate.to"]()}
+ (value = { ...value, to: v })}
+ >
+
+ {getSelectedTime(value.to as keyof typeof times)}
+
+
+ {#each Object.entries(times) as [time, value] (time)}
+ {value.label}
+ {/each}
+
+
+ {/snippet}
+
+
+
+
+
+
+ {#snippet children({ props })}
+ {m["components.slotTemplate.duration"]()}
+ (value = { ...value, duration: parseInt(v) })}
+ >
+
+ {getSelectedDuration()}
+
+
+ {#each Object.entries(durations) as [duration, value] (duration)}
+ {value.label}
+ {/each}
+
+
+ {/snippet}
+
+
+
+
diff --git a/src/lib/components/ui/slot-template/utils.ts b/src/lib/components/ui/slot-template/utils.ts
new file mode 100644
index 0000000..705934b
--- /dev/null
+++ b/src/lib/components/ui/slot-template/utils.ts
@@ -0,0 +1,124 @@
+import { m } from "$i18n/messages";
+
+export const weekdays = [
+ {
+ day: m["components.slotTemplate.weekdays_long.monday"](),
+ short: m["components.slotTemplate.weekdays_short.monday"](),
+ bit: 1,
+ },
+ {
+ day: m["components.slotTemplate.weekdays_long.tuesday"](),
+ short: m["components.slotTemplate.weekdays_short.tuesday"](),
+ bit: 2,
+ },
+ {
+ day: m["components.slotTemplate.weekdays_long.wednesday"](),
+ short: m["components.slotTemplate.weekdays_short.wednesday"](),
+ bit: 4,
+ },
+ {
+ day: m["components.slotTemplate.weekdays_long.thursday"](),
+ short: m["components.slotTemplate.weekdays_short.thursday"](),
+ bit: 8,
+ },
+ {
+ day: m["components.slotTemplate.weekdays_long.friday"](),
+ short: m["components.slotTemplate.weekdays_short.friday"](),
+ bit: 16,
+ },
+ {
+ day: m["components.slotTemplate.weekdays_long.saturday"](),
+ short: m["components.slotTemplate.weekdays_short.saturday"](),
+ bit: 32,
+ },
+ {
+ day: m["components.slotTemplate.weekdays_long.sunday"](),
+ short: m["components.slotTemplate.weekdays_short.sunday"](),
+ bit: 64,
+ },
+];
+
+export const durations = {
+ 5: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 5 }) },
+ 10: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 10 }) },
+ 15: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 15 }) },
+ 20: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 20 }) },
+ 25: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 25 }) },
+ 30: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 30 }) },
+ 45: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 45 }) },
+ 60: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 60 }) },
+ 90: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 90 }) },
+ 120: { label: m["components.slotTemplate.every_n_minutes"]({ minutes: 120 }) },
+} as const;
+
+export const times = {
+ "05:00:00": { label: m["components.slotTemplate.times.0500"]() },
+ "05:15:00": { label: m["components.slotTemplate.times.0515"]() },
+ "05:30:00": { label: m["components.slotTemplate.times.0530"]() },
+ "05:45:00": { label: m["components.slotTemplate.times.0545"]() },
+ "06:00:00": { label: m["components.slotTemplate.times.0600"]() },
+ "06:15:00": { label: m["components.slotTemplate.times.0615"]() },
+ "06:30:00": { label: m["components.slotTemplate.times.0630"]() },
+ "06:45:00": { label: m["components.slotTemplate.times.0645"]() },
+ "07:00:00": { label: m["components.slotTemplate.times.0700"]() },
+ "07:15:00": { label: m["components.slotTemplate.times.0715"]() },
+ "07:30:00": { label: m["components.slotTemplate.times.0730"]() },
+ "07:45:00": { label: m["components.slotTemplate.times.0745"]() },
+ "08:00:00": { label: m["components.slotTemplate.times.0800"]() },
+ "08:15:00": { label: m["components.slotTemplate.times.0815"]() },
+ "08:30:00": { label: m["components.slotTemplate.times.0830"]() },
+ "08:45:00": { label: m["components.slotTemplate.times.0845"]() },
+ "09:00:00": { label: m["components.slotTemplate.times.0900"]() },
+ "09:15:00": { label: m["components.slotTemplate.times.0915"]() },
+ "09:30:00": { label: m["components.slotTemplate.times.0930"]() },
+ "09:45:00": { label: m["components.slotTemplate.times.0945"]() },
+ "10:00:00": { label: m["components.slotTemplate.times.1000"]() },
+ "10:15:00": { label: m["components.slotTemplate.times.1015"]() },
+ "10:30:00": { label: m["components.slotTemplate.times.1030"]() },
+ "10:45:00": { label: m["components.slotTemplate.times.1045"]() },
+ "11:00:00": { label: m["components.slotTemplate.times.1100"]() },
+ "11:15:00": { label: m["components.slotTemplate.times.1115"]() },
+ "11:30:00": { label: m["components.slotTemplate.times.1130"]() },
+ "11:45:00": { label: m["components.slotTemplate.times.1145"]() },
+ "12:00:00": { label: m["components.slotTemplate.times.1200"]() },
+ "12:15:00": { label: m["components.slotTemplate.times.1215"]() },
+ "12:30:00": { label: m["components.slotTemplate.times.1230"]() },
+ "12:45:00": { label: m["components.slotTemplate.times.1245"]() },
+ "13:00:00": { label: m["components.slotTemplate.times.1300"]() },
+ "13:15:00": { label: m["components.slotTemplate.times.1315"]() },
+ "13:30:00": { label: m["components.slotTemplate.times.1330"]() },
+ "13:45:00": { label: m["components.slotTemplate.times.1345"]() },
+ "14:00:00": { label: m["components.slotTemplate.times.1400"]() },
+ "14:15:00": { label: m["components.slotTemplate.times.1415"]() },
+ "14:30:00": { label: m["components.slotTemplate.times.1430"]() },
+ "14:45:00": { label: m["components.slotTemplate.times.1445"]() },
+ "15:00:00": { label: m["components.slotTemplate.times.1500"]() },
+ "15:15:00": { label: m["components.slotTemplate.times.1515"]() },
+ "15:30:00": { label: m["components.slotTemplate.times.1530"]() },
+ "15:45:00": { label: m["components.slotTemplate.times.1545"]() },
+ "16:00:00": { label: m["components.slotTemplate.times.1600"]() },
+ "16:15:00": { label: m["components.slotTemplate.times.1615"]() },
+ "16:30:00": { label: m["components.slotTemplate.times.1630"]() },
+ "16:45:00": { label: m["components.slotTemplate.times.1645"]() },
+ "17:00:00": { label: m["components.slotTemplate.times.1700"]() },
+ "17:15:00": { label: m["components.slotTemplate.times.1715"]() },
+ "17:30:00": { label: m["components.slotTemplate.times.1730"]() },
+ "17:45:00": { label: m["components.slotTemplate.times.1745"]() },
+ "18:00:00": { label: m["components.slotTemplate.times.1800"]() },
+ "18:15:00": { label: m["components.slotTemplate.times.1815"]() },
+ "18:30:00": { label: m["components.slotTemplate.times.1830"]() },
+ "18:45:00": { label: m["components.slotTemplate.times.1845"]() },
+ "19:00:00": { label: m["components.slotTemplate.times.1900"]() },
+ "19:15:00": { label: m["components.slotTemplate.times.1915"]() },
+ "19:30:00": { label: m["components.slotTemplate.times.1930"]() },
+ "19:45:00": { label: m["components.slotTemplate.times.1945"]() },
+ "20:00:00": { label: m["components.slotTemplate.times.2000"]() },
+ "20:15:00": { label: m["components.slotTemplate.times.2015"]() },
+ "20:30:00": { label: m["components.slotTemplate.times.2030"]() },
+ "20:45:00": { label: m["components.slotTemplate.times.2045"]() },
+ "21:00:00": { label: m["components.slotTemplate.times.2100"]() },
+ "21:15:00": { label: m["components.slotTemplate.times.2115"]() },
+ "21:30:00": { label: m["components.slotTemplate.times.2130"]() },
+ "21:45:00": { label: m["components.slotTemplate.times.2145"]() },
+ "22:00:00": { label: m["components.slotTemplate.times.2200"]() },
+} as const;
diff --git a/src/lib/server/services/__tests__/channel-service.test.ts b/src/lib/server/services/__tests__/channel-service.test.ts
index ed82f55..5e0fabb 100644
--- a/src/lib/server/services/__tests__/channel-service.test.ts
+++ b/src/lib/server/services/__tests__/channel-service.test.ts
@@ -141,8 +141,8 @@ describe("ChannelService", () => {
slotTemplates: [
{
name: "Test Slot",
- from: "09:00",
- to: "17:00",
+ from: "09:00:00",
+ to: "17:00:00",
duration: 30,
},
],
@@ -172,7 +172,7 @@ describe("ChannelService", () => {
{
name: "Test Slot",
from: "invalid-time",
- to: "17:00",
+ to: "17:00:00",
duration: 30,
},
],
diff --git a/src/lib/server/services/channel-service.ts b/src/lib/server/services/channel-service.ts
index 0c6499b..43e43ed 100644
--- a/src/lib/server/services/channel-service.ts
+++ b/src/lib/server/services/channel-service.ts
@@ -1,13 +1,12 @@
-import { getTenantDb } from "../db";
-import * as tenantSchema from "../db/tenant-schema";
-import { type SelectChannel, type SelectSlotTemplate, type SelectAgent } from "../db/tenant-schema";
-
-import { eq, inArray } from "drizzle-orm";
-import logger from "$lib/logger";
-import z from "zod/v4";
-import { ValidationError, NotFoundError } from "../utils/errors";
-import { TenantConfig } from "../db/tenant-config";
import { supportedLocales } from "$lib/const/locales";
+import logger from "$lib/logger";
+import { asc, eq, inArray, sql } from "drizzle-orm";
+import z from "zod/v4";
+import { getTenantDb } from "../db";
+import { TenantConfig } from "../db/tenant-config";
+import * as tenantSchema from "../db/tenant-schema";
+import { type SelectAgent, type SelectChannel, type SelectSlotTemplate } from "../db/tenant-schema";
+import { NotFoundError, ValidationError } from "../utils/errors";
const CHANNEL_COLORS = ["#FF0000", "#00FF00", "#0000FF"] as const;
const NEXT_COLOR_KEY = "nextChannelColor";
@@ -15,8 +14,8 @@ const NEXT_COLOR_KEY = "nextChannelColor";
const slotTemplateSchema = z.object({
name: z.string().min(1).max(100),
weekdays: z.number().int().min(0).max(127).optional(),
- from: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/),
- to: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/),
+ from: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/),
+ to: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/),
duration: z.number().int().min(1).max(1440),
});
@@ -552,10 +551,12 @@ export class ChannelService {
const db = await this.getDb();
// Get all channels
+ // TODO: What should be the locale used here? Ideally it should be the users locale
+ const language = supportedLocales[0];
const channels = await db
.select()
.from(tenantSchema.channel)
- .orderBy(tenantSchema.channel.names);
+ .orderBy(asc(sql`${tenantSchema.channel.names}->>${language}`));
// Get relations for each channel
const result: ChannelWithRelations[] = [];
diff --git a/src/lib/stores/agents.ts b/src/lib/stores/agents.ts
new file mode 100644
index 0000000..a19bcf5
--- /dev/null
+++ b/src/lib/stores/agents.ts
@@ -0,0 +1,52 @@
+import { browser } from "$app/environment";
+import type { TAgent } from "$lib/types/agent";
+import { writable } from "svelte/store";
+import { auth } from "./auth";
+
+interface AgentsState {
+ agents: TAgent[];
+ isLoading: boolean;
+}
+
+const createAgentsStore = () => {
+ const store = writable