feat: configure overview camera per curriculum week
This commit is contained in:
@@ -9,6 +9,7 @@ export type CurriculumWeek = {
|
|||||||
duration: string;
|
duration: string;
|
||||||
github: string;
|
github: string;
|
||||||
detail: {
|
detail: {
|
||||||
|
camera?: { url: string; password: string };
|
||||||
overview: string;
|
overview: string;
|
||||||
learningSteps?: Array<{
|
learningSteps?: Array<{
|
||||||
title: string;
|
title: string;
|
||||||
@@ -145,6 +146,8 @@ export async function updateInstructorCurriculum(
|
|||||||
title?: string;
|
title?: string;
|
||||||
duration?: string;
|
duration?: string;
|
||||||
overview?: string;
|
overview?: string;
|
||||||
|
cameraViewUrl?: string;
|
||||||
|
cameraPassword?: string;
|
||||||
deliverable?: string;
|
deliverable?: string;
|
||||||
isTeamProject?: boolean;
|
isTeamProject?: boolean;
|
||||||
topics?: string[];
|
topics?: string[];
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export type InstructorPlatform = {
|
export type InstructorPlatform = {
|
||||||
camera?: { url: string; password: string };
|
|
||||||
id: string;
|
id: string;
|
||||||
masterPlatformId: string;
|
masterPlatformId: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -191,8 +190,6 @@ export async function updateInstructorPlatform(
|
|||||||
algorithms: PlatformAlgorithms,
|
algorithms: PlatformAlgorithms,
|
||||||
controlDurationMinutes: number,
|
controlDurationMinutes: number,
|
||||||
controlWarningMinutes: number,
|
controlWarningMinutes: number,
|
||||||
cameraViewUrl: string,
|
|
||||||
cameraPassword: string,
|
|
||||||
): Promise<InstructorPlatformsResponse> {
|
): Promise<InstructorPlatformsResponse> {
|
||||||
const response = await fetch(`${LMS_API_BASE_URL}/instructor/platforms/${platformId}`, {
|
const response = await fetch(`${LMS_API_BASE_URL}/instructor/platforms/${platformId}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@@ -210,8 +207,6 @@ export async function updateInstructorPlatform(
|
|||||||
algorithms,
|
algorithms,
|
||||||
controlDurationMinutes,
|
controlDurationMinutes,
|
||||||
controlWarningMinutes,
|
controlWarningMinutes,
|
||||||
cameraViewUrl,
|
|
||||||
cameraPassword,
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
|||||||
const [draftTitle, setDraftTitle] = useState('');
|
const [draftTitle, setDraftTitle] = useState('');
|
||||||
const [draftDuration, setDraftDuration] = useState('');
|
const [draftDuration, setDraftDuration] = useState('');
|
||||||
const [draftOverview, setDraftOverview] = useState('');
|
const [draftOverview, setDraftOverview] = useState('');
|
||||||
|
const [draftCameraUrl, setDraftCameraUrl] = useState('');
|
||||||
|
const [draftCameraPassword, setDraftCameraPassword] = useState('');
|
||||||
const [draftDeliverable, setDraftDeliverable] = useState('');
|
const [draftDeliverable, setDraftDeliverable] = useState('');
|
||||||
const [draftIsTeamProject, setDraftIsTeamProject] = useState(false);
|
const [draftIsTeamProject, setDraftIsTeamProject] = useState(false);
|
||||||
const [draftTopics, setDraftTopics] = useState<string[]>([]);
|
const [draftTopics, setDraftTopics] = useState<string[]>([]);
|
||||||
@@ -274,6 +276,8 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
|||||||
setDraftTitle(selectedWeek.title);
|
setDraftTitle(selectedWeek.title);
|
||||||
setDraftDuration(selectedWeek.duration);
|
setDraftDuration(selectedWeek.duration);
|
||||||
setDraftOverview(selectedWeek.detail.overview);
|
setDraftOverview(selectedWeek.detail.overview);
|
||||||
|
setDraftCameraUrl(selectedWeek.detail.camera?.url ?? '');
|
||||||
|
setDraftCameraPassword(selectedWeek.detail.camera?.password ?? '');
|
||||||
setDraftDeliverable(selectedWeek.detail.deliverable);
|
setDraftDeliverable(selectedWeek.detail.deliverable);
|
||||||
setDraftIsTeamProject(Boolean(selectedWeek.isTeamProject));
|
setDraftIsTeamProject(Boolean(selectedWeek.isTeamProject));
|
||||||
setDraftTopics(selectedWeek.topics.length ? selectedWeek.topics : ['']);
|
setDraftTopics(selectedWeek.topics.length ? selectedWeek.topics : ['']);
|
||||||
@@ -350,6 +354,8 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
|||||||
title: draftTitle,
|
title: draftTitle,
|
||||||
duration: draftDuration,
|
duration: draftDuration,
|
||||||
overview: draftOverview,
|
overview: draftOverview,
|
||||||
|
cameraViewUrl: draftCameraUrl,
|
||||||
|
cameraPassword: draftCameraPassword,
|
||||||
deliverable: draftDeliverable,
|
deliverable: draftDeliverable,
|
||||||
isTeamProject: draftIsTeamProject,
|
isTeamProject: draftIsTeamProject,
|
||||||
topics: draftTopics.map((topic) => topic.trim()).filter(Boolean),
|
topics: draftTopics.map((topic) => topic.trim()).filter(Boolean),
|
||||||
@@ -398,6 +404,8 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
|||||||
title: draftTitle,
|
title: draftTitle,
|
||||||
duration: draftDuration,
|
duration: draftDuration,
|
||||||
overview: draftOverview,
|
overview: draftOverview,
|
||||||
|
cameraViewUrl: draftCameraUrl,
|
||||||
|
cameraPassword: draftCameraPassword,
|
||||||
deliverable: draftDeliverable,
|
deliverable: draftDeliverable,
|
||||||
isTeamProject: draftIsTeamProject,
|
isTeamProject: draftIsTeamProject,
|
||||||
topics,
|
topics,
|
||||||
@@ -439,6 +447,8 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
|||||||
title: draftTitle,
|
title: draftTitle,
|
||||||
duration: draftDuration,
|
duration: draftDuration,
|
||||||
overview: draftOverview,
|
overview: draftOverview,
|
||||||
|
cameraViewUrl: draftCameraUrl,
|
||||||
|
cameraPassword: draftCameraPassword,
|
||||||
deliverable: draftDeliverable,
|
deliverable: draftDeliverable,
|
||||||
isTeamProject: draftIsTeamProject,
|
isTeamProject: draftIsTeamProject,
|
||||||
topics: draftTopics.map((topic) => topic.trim()).filter(Boolean),
|
topics: draftTopics.map((topic) => topic.trim()).filter(Boolean),
|
||||||
@@ -1205,6 +1215,19 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
|||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid min-w-0 gap-3">
|
||||||
|
<label className="block min-w-0">
|
||||||
|
<span className="text-[12px] font-black text-[#475569]">오버뷰 시청 URL</span>
|
||||||
|
<input type="url" value={draftCameraUrl} onChange={(event) => setDraftCameraUrl(event.target.value)}
|
||||||
|
placeholder="시청 링크"
|
||||||
|
className="mt-1.5 h-10 w-full min-w-0 rounded-md border border-[#d1d9e6] bg-white px-3 text-[13px] outline-none focus:border-[#2563eb]" />
|
||||||
|
</label>
|
||||||
|
<label className="block min-w-0">
|
||||||
|
<span className="text-[12px] font-black text-[#475569]">오버뷰 방 비밀번호</span>
|
||||||
|
<input type="password" autoComplete="new-password" value={draftCameraPassword} onChange={(event) => setDraftCameraPassword(event.target.value)}
|
||||||
|
className="mt-1.5 h-10 w-full min-w-0 rounded-md border border-[#d1d9e6] bg-white px-3 text-[13px] outline-none focus:border-[#2563eb]" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<label className="block h-[100px]">
|
<label className="block h-[100px]">
|
||||||
<span className="text-[12px] font-black text-[#475569]">주차 개요</span>
|
<span className="text-[12px] font-black text-[#475569]">주차 개요</span>
|
||||||
<textarea
|
<textarea
|
||||||
|
|||||||
@@ -86,8 +86,6 @@ export function PlatformsPage({ session }: { session: AuthSession }) {
|
|||||||
});
|
});
|
||||||
const [settingsControlDurationMinutes, setSettingsControlDurationMinutes] = useState(30);
|
const [settingsControlDurationMinutes, setSettingsControlDurationMinutes] = useState(30);
|
||||||
const [settingsControlWarningMinutes, setSettingsControlWarningMinutes] = useState(5);
|
const [settingsControlWarningMinutes, setSettingsControlWarningMinutes] = useState(5);
|
||||||
const [settingsCameraUrl, setSettingsCameraUrl] = useState('');
|
|
||||||
const [settingsCameraPassword, setSettingsCameraPassword] = useState('');
|
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [deletingPlatformId, setDeletingPlatformId] = useState<string | null>(null);
|
const [deletingPlatformId, setDeletingPlatformId] = useState<string | null>(null);
|
||||||
const [deleteTarget, setDeleteTarget] = useState<InstructorPlatform | null>(null);
|
const [deleteTarget, setDeleteTarget] = useState<InstructorPlatform | null>(null);
|
||||||
@@ -197,8 +195,6 @@ export function PlatformsPage({ session }: { session: AuthSession }) {
|
|||||||
setSettingsAlgorithms(settingsTarget?.algorithms ?? defaultAlgorithms);
|
setSettingsAlgorithms(settingsTarget?.algorithms ?? defaultAlgorithms);
|
||||||
setSettingsControlDurationMinutes(settingsTarget?.controlDurationMinutes ?? 30);
|
setSettingsControlDurationMinutes(settingsTarget?.controlDurationMinutes ?? 30);
|
||||||
setSettingsControlWarningMinutes(settingsTarget?.controlWarningMinutes ?? 5);
|
setSettingsControlWarningMinutes(settingsTarget?.controlWarningMinutes ?? 5);
|
||||||
setSettingsCameraUrl(settingsTarget?.camera?.url ?? '');
|
|
||||||
setSettingsCameraPassword(settingsTarget?.camera?.password ?? '');
|
|
||||||
}, [settingsTarget]);
|
}, [settingsTarget]);
|
||||||
|
|
||||||
const handlePageSizeChange = (nextPageSize: number) => {
|
const handlePageSizeChange = (nextPageSize: number) => {
|
||||||
@@ -383,8 +379,6 @@ export function PlatformsPage({ session }: { session: AuthSession }) {
|
|||||||
settingsAlgorithms,
|
settingsAlgorithms,
|
||||||
settingsControlDurationMinutes,
|
settingsControlDurationMinutes,
|
||||||
settingsControlWarningMinutes,
|
settingsControlWarningMinutes,
|
||||||
settingsCameraUrl,
|
|
||||||
settingsCameraPassword,
|
|
||||||
);
|
);
|
||||||
applyPayload(payload);
|
applyPayload(payload);
|
||||||
showToast({ title: '플랫폼 설정이 저장되었습니다.', message: nextName });
|
showToast({ title: '플랫폼 설정이 저장되었습니다.', message: nextName });
|
||||||
@@ -861,19 +855,6 @@ export function PlatformsPage({ session }: { session: AuthSession }) {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SettingsSection title="주행장 카메라">
|
|
||||||
<div className="grid min-w-0 gap-3">
|
|
||||||
<SettingsField label="VDO.Ninja 시청 URL">
|
|
||||||
<input type="url" value={settingsCameraUrl} onChange={(event) => setSettingsCameraUrl(event.target.value)}
|
|
||||||
placeholder="https://vdo.ninja/?scene&room=ROOM&view=STREAM"
|
|
||||||
className="h-10 w-full min-w-0 rounded-md border border-[#d1d9e6] px-3 text-[13px] outline-none focus:border-[#2563eb]" />
|
|
||||||
</SettingsField>
|
|
||||||
<SettingsField label="방 비밀번호">
|
|
||||||
<input type="password" autoComplete="new-password" value={settingsCameraPassword} onChange={(event) => setSettingsCameraPassword(event.target.value)}
|
|
||||||
className="h-10 w-full min-w-0 rounded-md border border-[#d1d9e6] px-3 text-[13px] outline-none focus:border-[#2563eb]" />
|
|
||||||
</SettingsField>
|
|
||||||
</div>
|
|
||||||
</SettingsSection>
|
|
||||||
<SettingsSection title="제어 시간 설정">
|
<SettingsSection title="제어 시간 설정">
|
||||||
<div className="grid gap-3 md:grid-cols-2">
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
<SettingsField label="1회 제어 시간 (분)">
|
<SettingsField label="1회 제어 시간 (분)">
|
||||||
|
|||||||
Reference in New Issue
Block a user