224 lines
7.8 KiB
TypeScript
224 lines
7.8 KiB
TypeScript
import { Plus, Save, X } from 'lucide-react';
|
|
|
|
import type {
|
|
ClassroomStrategyDraft,
|
|
ClassroomSupportPage,
|
|
} from '@/business/classroom-support/types';
|
|
import { Button } from '@/components/ui/button';
|
|
import { ImageUpload } from '@/components/common/ImageUpload';
|
|
import { Input } from '@/components/ui/input';
|
|
import { NativeSelect } from '@/components/ui/native-select';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import {
|
|
CLASSROOM_SUPPORT_AGE_FILTERS,
|
|
CLASSROOM_SUPPORT_CATEGORY_FILTERS,
|
|
CLASSROOM_SUPPORT_ZONE_FILTERS,
|
|
} from '@/shared/constants/classroomSupport';
|
|
import type { Strategy } from '@/shared/types/app';
|
|
|
|
interface ClassroomSupportManagementPanelProps {
|
|
readonly page: ClassroomSupportPage;
|
|
}
|
|
|
|
type DraftField = keyof ClassroomStrategyDraft;
|
|
|
|
function toStrategyCategory(value: string): Strategy['category'] {
|
|
switch (value) {
|
|
case 'transition':
|
|
case 'sensory':
|
|
case 'communication':
|
|
case 'behavior':
|
|
case 'social':
|
|
return value;
|
|
case 'visual-support':
|
|
default:
|
|
return 'visual-support';
|
|
}
|
|
}
|
|
|
|
function toStrategyAgeGroup(value: string): Strategy['ageGroup'] {
|
|
switch (value) {
|
|
case 'K-2':
|
|
case '3-5':
|
|
case '6-8':
|
|
return value;
|
|
case 'All':
|
|
default:
|
|
return 'All';
|
|
}
|
|
}
|
|
|
|
function toStrategyZone(value: string): Strategy['zone'] {
|
|
switch (value) {
|
|
case 'blue':
|
|
case 'yellow':
|
|
case 'red':
|
|
return value;
|
|
case 'green':
|
|
default:
|
|
return 'green';
|
|
}
|
|
}
|
|
|
|
export function ClassroomSupportManagementPanel({ page }: ClassroomSupportManagementPanelProps) {
|
|
if (!page.canManageStrategies) {
|
|
return null;
|
|
}
|
|
|
|
const draft = page.strategyDraft;
|
|
|
|
function updateTextField(field: DraftField, value: string) {
|
|
page.updateStrategyDraft({ [field]: value });
|
|
}
|
|
|
|
return (
|
|
<section className="rounded-2xl border border-slate-700/50 bg-slate-900/45 overflow-hidden">
|
|
<div className="flex flex-col gap-3 border-b border-slate-700/50 px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
|
|
<div>
|
|
<h3 className="text-lg font-semibold text-white">Manage Strategy Cards</h3>
|
|
<p className="text-sm text-slate-400">Organization-level classroom support content</p>
|
|
</div>
|
|
<Button
|
|
type="button"
|
|
onClick={page.startCreateStrategy}
|
|
disabled={page.isSavingStrategies}
|
|
leadingIcon={<Plus size={16} />}
|
|
className="bg-emerald-600 hover:bg-emerald-500 text-white"
|
|
>
|
|
Add Strategy
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-5 p-5">
|
|
{draft && (
|
|
<div className="rounded-xl border border-slate-700/60 bg-slate-950/35 p-4">
|
|
<div className="mb-4 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
|
<h4 className="text-base font-semibold text-white">
|
|
{page.strategyDraftMode === 'create' ? 'New Strategy' : 'Edit Strategy'}
|
|
</h4>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={page.cancelStrategyDraft}
|
|
leadingIcon={<X size={16} />}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid gap-4 lg:grid-cols-2">
|
|
<label className="space-y-2 text-sm font-medium text-slate-300">
|
|
Title
|
|
<Input
|
|
value={draft.title}
|
|
onChange={(event) => updateTextField('title', event.target.value)}
|
|
placeholder="Strategy title"
|
|
/>
|
|
</label>
|
|
<ImageUpload
|
|
value={draft.image || null}
|
|
onChange={(privateUrl) => page.updateStrategyDraft({ image: privateUrl ?? '' })}
|
|
table="classroom-support"
|
|
field="strategy-images"
|
|
label="Strategy image"
|
|
/>
|
|
<label className="space-y-2 text-sm font-medium text-slate-300 lg:col-span-2">
|
|
Description
|
|
<Textarea
|
|
value={draft.description}
|
|
onChange={(event) => updateTextField('description', event.target.value)}
|
|
placeholder="Short card description"
|
|
/>
|
|
</label>
|
|
<label className="space-y-2 text-sm font-medium text-slate-300 lg:col-span-2">
|
|
Implementation Tip
|
|
<Textarea
|
|
value={draft.implementationTip}
|
|
onChange={(event) => updateTextField('implementationTip', event.target.value)}
|
|
placeholder="Practical implementation detail"
|
|
/>
|
|
</label>
|
|
<label className="space-y-2 text-sm font-medium text-slate-300">
|
|
Category
|
|
<NativeSelect
|
|
value={draft.category}
|
|
onChange={(event) => page.updateStrategyDraft({ category: toStrategyCategory(event.target.value) })}
|
|
>
|
|
{CLASSROOM_SUPPORT_CATEGORY_FILTERS
|
|
.filter((category) => category.value !== 'all')
|
|
.map((category) => (
|
|
<option key={category.value} value={category.value}>
|
|
{category.label}
|
|
</option>
|
|
))}
|
|
</NativeSelect>
|
|
</label>
|
|
<label className="space-y-2 text-sm font-medium text-slate-300">
|
|
Age Group
|
|
<NativeSelect
|
|
value={draft.ageGroup}
|
|
onChange={(event) => page.updateStrategyDraft({ ageGroup: toStrategyAgeGroup(event.target.value) })}
|
|
>
|
|
{CLASSROOM_SUPPORT_AGE_FILTERS
|
|
.filter((age) => age.value !== 'all')
|
|
.map((age) => (
|
|
<option key={age.value} value={age.value}>
|
|
{age.label}
|
|
</option>
|
|
))}
|
|
</NativeSelect>
|
|
</label>
|
|
<label className="space-y-2 text-sm font-medium text-slate-300">
|
|
Zone
|
|
<NativeSelect
|
|
value={draft.zone}
|
|
onChange={(event) => page.updateStrategyDraft({ zone: toStrategyZone(event.target.value) })}
|
|
>
|
|
{CLASSROOM_SUPPORT_ZONE_FILTERS
|
|
.filter((zone) => zone.value !== 'all')
|
|
.map((zone) => (
|
|
<option key={zone.value} value={zone.value}>
|
|
{zone.label}
|
|
</option>
|
|
))}
|
|
</NativeSelect>
|
|
</label>
|
|
</div>
|
|
|
|
{page.strategyDraftError && (
|
|
<p className="mt-4 rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm font-medium text-red-300">
|
|
{page.strategyDraftError}
|
|
</p>
|
|
)}
|
|
|
|
<div className="mt-5 flex justify-end">
|
|
<Button
|
|
type="button"
|
|
onClick={page.saveStrategyDraft}
|
|
loading={page.isSavingStrategies}
|
|
leadingIcon={<Save size={16} />}
|
|
className="bg-blue-600 hover:bg-blue-500 text-white"
|
|
>
|
|
Save Strategy
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{Boolean(page.strategyManagementError) && (
|
|
<p className="rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm font-medium text-red-300">
|
|
Classroom support cards could not be saved.
|
|
</p>
|
|
)}
|
|
{page.strategySaveMessage && (
|
|
<p className="rounded-lg border border-emerald-500/30 bg-emerald-500/10 px-3 py-2 text-sm font-medium text-emerald-300">
|
|
{page.strategySaveMessage}
|
|
</p>
|
|
)}
|
|
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|