Skip to content

Commit a093bcb

Browse files
committed
Add Ongeki challenge songs, admin tab and themed date picker
- Show each event's chapter name and its challenge song list, and localize the whole challenge page in English and Chinese - Move the event editor into an admin tab with editable type, KoP id, dates and song list, and drop the standalone route plus its sidebar link - Add DateTimeField on top of the themed date and time pickers instead of the native browser input, and render dates without timezone timestamps - Let a wrapped event title stop stretching the badge row
1 parent dbb3310 commit a093bcb

11 files changed

Lines changed: 754 additions & 186 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { DatePicker, TimePicker } from 'animal-island-ui';
2+
import { LiquidTextField } from '@liquefy-ui/react';
3+
import { useTheme } from '@/lib/theme';
4+
5+
/**
6+
* Date + time input for the admin screens.
7+
*
8+
* Each interface style gets its own control: Animal Island UI ships a themed
9+
* picker pair, Liquid Glass has its own text field, and the classic theme keeps
10+
* the browser's native control with the Bootstrap input classes.
11+
*/
12+
interface DateTimeFieldProps {
13+
/** Value in the native `datetime-local` format, `YYYY-MM-DDTHH:mm`. */
14+
value: string;
15+
onChange: (value: string) => void;
16+
disabled?: boolean;
17+
label?: string;
18+
}
19+
20+
function splitDateTime(value: string): { date: string; time: string } {
21+
const [date = '', time = ''] = value.split('T');
22+
return { date, time: time.slice(0, 5) };
23+
}
24+
25+
function joinDateTime(date: string, time: string): string {
26+
if (!date) return '';
27+
return `${date}T${time || '00:00'}`;
28+
}
29+
30+
export function DateTimeField({ value, onChange, disabled, label }: DateTimeFieldProps) {
31+
const { family } = useTheme();
32+
const { date, time } = splitDateTime(value);
33+
34+
if (family === 'animal-island') {
35+
return (
36+
<div className="d-flex flex-wrap gap-1" role="group" aria-label={label}>
37+
<DatePicker
38+
size="small"
39+
value={date || null}
40+
disabled={disabled}
41+
aria-label={label}
42+
onChange={(next) => onChange(joinDateTime(typeof next === 'string' ? next : '', time))}
43+
/>
44+
<TimePicker
45+
size="small"
46+
format="HH:mm"
47+
value={time || undefined}
48+
disabled={disabled}
49+
allowClear={false}
50+
aria-label={label}
51+
onChange={(next) => onChange(joinDateTime(date, (next ?? '').slice(0, 5)))}
52+
/>
53+
</div>
54+
);
55+
}
56+
57+
if (family === 'liquefy') {
58+
return (
59+
<LiquidTextField
60+
type="datetime-local"
61+
value={value}
62+
disabled={disabled}
63+
aria-label={label}
64+
onChange={(event) => onChange(event.target.value)}
65+
/>
66+
);
67+
}
68+
69+
return (
70+
<input
71+
className="form-control form-control-sm"
72+
type="datetime-local"
73+
value={value}
74+
disabled={disabled}
75+
aria-label={label}
76+
onChange={(event) => onChange(event.target.value)}
77+
/>
78+
);
79+
}

‎src/components/shell/AppShell.tsx‎

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,11 @@ function SidebarNav({ onNavigate }: { onNavigate?: () => void }) {
140140
</a>
141141
</li>
142142
{admin && (
143-
<>
144-
<li className="pb-2">
145-
<a className={'link-btn rounded' + (isActive('admin') ? ' active' : '')} onClick={() => go('admin')}>
146-
{t('App.Sidebar.Admin')}
147-
</a>
148-
</li>
149-
<li className="pb-2">
150-
<a className={'link-btn rounded' + (isActive('admin/ongeki-events') ? ' active' : '')} onClick={() => go('admin/ongeki-events')}>
151-
Ongeki Events
152-
</a>
153-
</li>
154-
</>
143+
<li className="pb-2">
144+
<a className={'link-btn rounded' + (isActive('admin') ? ' active' : '')} onClick={() => go('admin')}>
145+
{t('App.Sidebar.Admin')}
146+
</a>
147+
</li>
155148
)}
156149
</ul>
157150
</li>

0 commit comments

Comments
 (0)