feat(frontend): ScheduleConfig V2 types, weekday schema, export update
This commit is contained in:
@@ -71,7 +71,9 @@ export function ChannelCard({
|
|||||||
onMoveDown,
|
onMoveDown,
|
||||||
}: ChannelCardProps) {
|
}: ChannelCardProps) {
|
||||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||||
const blockCount = channel.schedule_config.blocks.length;
|
const blockCount = Object.values(channel.schedule_config.day_blocks).reduce(
|
||||||
|
(sum, blocks) => sum + blocks.length, 0
|
||||||
|
);
|
||||||
const { status, label } = useScheduleStatus(channel.id);
|
const { status, label } = useScheduleStatus(channel.id);
|
||||||
|
|
||||||
const scheduleColor =
|
const scheduleColor =
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ import type {
|
|||||||
MediaFilter,
|
MediaFilter,
|
||||||
ProviderInfo,
|
ProviderInfo,
|
||||||
RecyclePolicy,
|
RecyclePolicy,
|
||||||
|
Weekday,
|
||||||
} from "@/lib/types";
|
} from "@/lib/types";
|
||||||
|
import { WEEKDAYS } from "@/lib/types";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Local shared primitives (only used inside this file)
|
// Local shared primitives (only used inside this file)
|
||||||
@@ -334,7 +336,7 @@ interface EditChannelSheetProps {
|
|||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
schedule_config: { blocks: ProgrammingBlock[] };
|
schedule_config: { day_blocks: Record<Weekday, ProgrammingBlock[]> };
|
||||||
recycle_policy: RecyclePolicy;
|
recycle_policy: RecyclePolicy;
|
||||||
auto_schedule: boolean;
|
auto_schedule: boolean;
|
||||||
access_mode?: AccessMode;
|
access_mode?: AccessMode;
|
||||||
@@ -373,7 +375,9 @@ export function EditChannelSheet({
|
|||||||
name: form.name,
|
name: form.name,
|
||||||
description: form.description,
|
description: form.description,
|
||||||
timezone: form.timezone,
|
timezone: form.timezone,
|
||||||
blocks: form.blocks,
|
day_blocks: Object.fromEntries(
|
||||||
|
WEEKDAYS.map(d => [d, d === 'monday' ? form.blocks : []])
|
||||||
|
) as Record<Weekday, ProgrammingBlock[]>,
|
||||||
recycle_policy: form.recyclePolicy,
|
recycle_policy: form.recyclePolicy,
|
||||||
auto_schedule: form.autoSchedule,
|
auto_schedule: form.autoSchedule,
|
||||||
access_mode: form.accessMode,
|
access_mode: form.accessMode,
|
||||||
@@ -390,7 +394,11 @@ export function EditChannelSheet({
|
|||||||
name: form.name,
|
name: form.name,
|
||||||
description: form.description,
|
description: form.description,
|
||||||
timezone: form.timezone,
|
timezone: form.timezone,
|
||||||
schedule_config: { blocks: form.blocks },
|
schedule_config: {
|
||||||
|
day_blocks: Object.fromEntries(
|
||||||
|
WEEKDAYS.map(d => [d, d === 'monday' ? form.blocks : []])
|
||||||
|
) as Record<Weekday, ProgrammingBlock[]>,
|
||||||
|
},
|
||||||
recycle_policy: form.recyclePolicy,
|
recycle_policy: form.recyclePolicy,
|
||||||
auto_schedule: form.autoSchedule,
|
auto_schedule: form.autoSchedule,
|
||||||
access_mode: form.accessMode !== "public" ? form.accessMode : "public",
|
access_mode: form.accessMode !== "public" ? form.accessMode : "public",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import type {
|
|||||||
ChannelResponse,
|
ChannelResponse,
|
||||||
ProgrammingBlock,
|
ProgrammingBlock,
|
||||||
RecyclePolicy,
|
RecyclePolicy,
|
||||||
|
Weekday,
|
||||||
} from "@/lib/types";
|
} from "@/lib/types";
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
@@ -84,7 +85,7 @@ export default function DashboardPage() {
|
|||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
schedule_config: { blocks: ProgrammingBlock[] };
|
schedule_config: { day_blocks: Record<Weekday, ProgrammingBlock[]> };
|
||||||
recycle_policy: RecyclePolicy;
|
recycle_policy: RecyclePolicy;
|
||||||
auto_schedule: boolean;
|
auto_schedule: boolean;
|
||||||
access_mode?: import("@/lib/types").AccessMode;
|
access_mode?: import("@/lib/types").AccessMode;
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function useChannelForm(channel: ChannelResponse | null) {
|
|||||||
setName(channel.name);
|
setName(channel.name);
|
||||||
setDescription(channel.description ?? "");
|
setDescription(channel.description ?? "");
|
||||||
setTimezone(channel.timezone);
|
setTimezone(channel.timezone);
|
||||||
setBlocks(channel.schedule_config.blocks);
|
setBlocks(channel.schedule_config.day_blocks['monday'] ?? []);
|
||||||
setRecyclePolicy(channel.recycle_policy);
|
setRecyclePolicy(channel.recycle_policy);
|
||||||
setAutoSchedule(channel.auto_schedule);
|
setAutoSchedule(channel.auto_schedule);
|
||||||
setAccessMode(channel.access_mode ?? "public");
|
setAccessMode(channel.access_mode ?? "public");
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { useState } from "react";
|
|||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { api } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
import type { ChannelImportData } from "@/app/(main)/dashboard/components/import-channel-dialog";
|
import type { ChannelImportData } from "@/app/(main)/dashboard/components/import-channel-dialog";
|
||||||
|
import { WEEKDAYS } from "@/lib/types";
|
||||||
|
import type { Weekday } from "@/lib/types";
|
||||||
|
|
||||||
export function useImportChannel(token: string | null) {
|
export function useImportChannel(token: string | null) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -26,7 +28,11 @@ export function useImportChannel(token: string | null) {
|
|||||||
await api.channels.update(
|
await api.channels.update(
|
||||||
created.id,
|
created.id,
|
||||||
{
|
{
|
||||||
schedule_config: { blocks: data.blocks },
|
schedule_config: {
|
||||||
|
day_blocks: Object.fromEntries(
|
||||||
|
WEEKDAYS.map(d => [d, d === 'monday' ? data.blocks : []])
|
||||||
|
) as Record<Weekday, typeof data.blocks>,
|
||||||
|
},
|
||||||
recycle_policy: data.recycle_policy,
|
recycle_policy: data.recycle_policy,
|
||||||
},
|
},
|
||||||
token,
|
token,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function exportChannel(channel: ChannelResponse): void {
|
|||||||
name: channel.name,
|
name: channel.name,
|
||||||
description: channel.description ?? undefined,
|
description: channel.description ?? undefined,
|
||||||
timezone: channel.timezone,
|
timezone: channel.timezone,
|
||||||
blocks: channel.schedule_config.blocks,
|
day_blocks: channel.schedule_config.day_blocks,
|
||||||
recycle_policy: channel.recycle_policy,
|
recycle_policy: channel.recycle_policy,
|
||||||
};
|
};
|
||||||
const blob = new Blob([JSON.stringify(payload, null, 2)], {
|
const blob = new Blob([JSON.stringify(payload, null, 2)], {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { WEEKDAYS } from "@/lib/types";
|
||||||
|
import type { Weekday } from "@/lib/types";
|
||||||
|
|
||||||
|
const weekdaySchema = z.enum([
|
||||||
|
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
|
||||||
|
]);
|
||||||
|
|
||||||
export const mediaFilterSchema = z.object({
|
export const mediaFilterSchema = z.object({
|
||||||
content_type: z.enum(["movie", "episode", "short"]).nullable().optional(),
|
content_type: z.enum(["movie", "episode", "short"]).nullable().optional(),
|
||||||
@@ -53,7 +59,10 @@ export const channelFormSchema = z.object({
|
|||||||
name: z.string().min(1, "Name is required"),
|
name: z.string().min(1, "Name is required"),
|
||||||
timezone: z.string().min(1, "Timezone is required"),
|
timezone: z.string().min(1, "Timezone is required"),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
blocks: z.array(blockSchema),
|
day_blocks: z.record(weekdaySchema, z.array(blockSchema))
|
||||||
|
.default(() =>
|
||||||
|
Object.fromEntries(WEEKDAYS.map(d => [d, []])) as unknown as Record<Weekday, z.infer<typeof blockSchema>[]>
|
||||||
|
),
|
||||||
recycle_policy: z.object({
|
recycle_policy: z.object({
|
||||||
cooldown_days: z.number().int().min(0).nullable().optional(),
|
cooldown_days: z.number().int().min(0).nullable().optional(),
|
||||||
cooldown_generations: z.number().int().min(0).nullable().optional(),
|
cooldown_generations: z.number().int().min(0).nullable().optional(),
|
||||||
|
|||||||
@@ -91,8 +91,35 @@ export interface ProgrammingBlock {
|
|||||||
access_password?: string;
|
access_password?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Weekday =
|
||||||
|
| 'monday' | 'tuesday' | 'wednesday' | 'thursday'
|
||||||
|
| 'friday' | 'saturday' | 'sunday'
|
||||||
|
|
||||||
|
export const WEEKDAYS: Weekday[] = [
|
||||||
|
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const WEEKDAY_LABELS: Record<Weekday, string> = {
|
||||||
|
monday: 'Mon', tuesday: 'Tue', wednesday: 'Wed', thursday: 'Thu',
|
||||||
|
friday: 'Fri', saturday: 'Sat', sunday: 'Sun',
|
||||||
|
}
|
||||||
|
|
||||||
export interface ScheduleConfig {
|
export interface ScheduleConfig {
|
||||||
blocks: ProgrammingBlock[];
|
day_blocks: Record<Weekday, ProgrammingBlock[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigSnapshot {
|
||||||
|
id: string
|
||||||
|
version_num: number
|
||||||
|
label: string | null
|
||||||
|
created_at: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScheduleHistoryEntry {
|
||||||
|
id: string
|
||||||
|
generation: number
|
||||||
|
valid_from: string
|
||||||
|
valid_until: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
|
|||||||
Reference in New Issue
Block a user