diff --git a/apps/admin-dashboard/src/components/source-config-panel.tsx b/apps/admin-dashboard/src/components/source-config-panel.tsx
index bb2b658..0816c2d 100644
--- a/apps/admin-dashboard/src/components/source-config-panel.tsx
+++ b/apps/admin-dashboard/src/components/source-config-panel.tsx
@@ -408,6 +408,40 @@ function FieldInput({
)
}
+ if (field.type === "multiselect" && field.options) {
+ const selected = Array.isArray(value) ? (value as string[]) : []
+
+ function toggle(optValue: string) {
+ const next = selected.includes(optValue)
+ ? selected.filter((v) => v !== optValue)
+ : [...selected, optValue]
+ onChange(next)
+ }
+
+ return (
+
+
+
+ {field.options!.map((opt) => {
+ const isSelected = selected.includes(opt.value)
+ return (
+ !disabled && toggle(opt.value)}
+ >
+ {opt.label}
+
+ )
+ })}
+
+
+ )
+ }
+
if (field.type === "number") {
return (
@@ -456,6 +490,8 @@ function buildInitialValues(
values[name] = saved[name]
} else if (field.defaultValue !== undefined) {
values[name] = field.defaultValue
+ } else if (field.type === "multiselect") {
+ values[name] = []
} else {
values[name] = field.type === "number" ? undefined : ""
}
diff --git a/apps/admin-dashboard/src/lib/api.ts b/apps/admin-dashboard/src/lib/api.ts
index 41ef9f0..772b534 100644
--- a/apps/admin-dashboard/src/lib/api.ts
+++ b/apps/admin-dashboard/src/lib/api.ts
@@ -9,12 +9,12 @@ function serverBase() {
}
export interface ConfigFieldDef {
- type: "string" | "number" | "select"
+ type: "string" | "number" | "select" | "multiselect"
label: string
required?: boolean
description?: string
secret?: boolean
- defaultValue?: string | number
+ defaultValue?: string | number | string[]
options?: { label: string; value: string }[]
}
@@ -54,6 +54,39 @@ const sourceDefinitions: SourceDefinition[] = [
dailyLimit: { type: "number", label: "Daily Forecast Limit", defaultValue: 7, description: "Number of daily forecasts to include" },
},
},
+ {
+ id: "aelis.tfl",
+ name: "TfL",
+ description: "Transport for London tube line status alerts.",
+ fields: {
+ lines: {
+ type: "multiselect",
+ label: "Lines",
+ description: "Lines to monitor. Leave empty for all lines.",
+ defaultValue: [],
+ options: [
+ { label: "Bakerloo", value: "bakerloo" },
+ { label: "Central", value: "central" },
+ { label: "Circle", value: "circle" },
+ { label: "District", value: "district" },
+ { label: "Hammersmith & City", value: "hammersmith-city" },
+ { label: "Jubilee", value: "jubilee" },
+ { label: "Metropolitan", value: "metropolitan" },
+ { label: "Northern", value: "northern" },
+ { label: "Piccadilly", value: "piccadilly" },
+ { label: "Victoria", value: "victoria" },
+ { label: "Waterloo & City", value: "waterloo-city" },
+ { label: "Lioness", value: "lioness" },
+ { label: "Mildmay", value: "mildmay" },
+ { label: "Windrush", value: "windrush" },
+ { label: "Weaver", value: "weaver" },
+ { label: "Suffragette", value: "suffragette" },
+ { label: "Liberty", value: "liberty" },
+ { label: "Elizabeth", value: "elizabeth" },
+ ],
+ },
+ },
+ },
]
export function fetchSources(): Promise {
diff --git a/apps/admin-dashboard/src/routes/_dashboard.tsx b/apps/admin-dashboard/src/routes/_dashboard.tsx
index bfbf00b..eba5b8f 100644
--- a/apps/admin-dashboard/src/routes/_dashboard.tsx
+++ b/apps/admin-dashboard/src/routes/_dashboard.tsx
@@ -6,6 +6,7 @@ import {
CircleDot,
CloudSun,
Loader2,
+ TrainFront,
LogOut,
MapPin,
Rss,
@@ -41,6 +42,7 @@ const SOURCE_ICONS: Record>
"aelis.weather": CloudSun,
"aelis.caldav": CalendarDays,
"aelis.google-calendar": Calendar,
+ "aelis.tfl": TrainFront,
}
export const Route = createRoute({