@hed-hog/studio 0.0.285
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/studio.controller.d.ts +79 -0
- package/dist/studio.controller.d.ts.map +1 -0
- package/dist/studio.controller.js +186 -0
- package/dist/studio.controller.js.map +1 -0
- package/dist/studio.module.d.ts +3 -0
- package/dist/studio.module.d.ts.map +1 -0
- package/dist/studio.module.js +33 -0
- package/dist/studio.module.js.map +1 -0
- package/dist/studio.service.d.ts +76 -0
- package/dist/studio.service.d.ts.map +1 -0
- package/dist/studio.service.js +98 -0
- package/dist/studio.service.js.map +1 -0
- package/hedhog/data/menu.yaml +114 -0
- package/hedhog/data/role.yaml +7 -0
- package/hedhog/data/route.yaml +152 -0
- package/hedhog/data/setting_group.yaml +8 -0
- package/hedhog/frontend/app/_components/studio-status-badge.tsx.ejs +14 -0
- package/hedhog/frontend/app/_lib/mocks.ts.ejs +209 -0
- package/hedhog/frontend/app/_lib/status.ts.ejs +38 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +148 -0
- package/hedhog/frontend/app/assets/page.tsx.ejs +117 -0
- package/hedhog/frontend/app/editing/page.tsx.ejs +55 -0
- package/hedhog/frontend/app/incidents/page.tsx.ejs +59 -0
- package/hedhog/frontend/app/page.tsx.ejs +207 -0
- package/hedhog/frontend/app/projects/[id]/page.tsx.ejs +323 -0
- package/hedhog/frontend/app/projects/form/page.tsx.ejs +129 -0
- package/hedhog/frontend/app/projects/page.tsx.ejs +169 -0
- package/hedhog/frontend/app/publication/page.tsx.ejs +62 -0
- package/hedhog/frontend/app/scenes/form/page.tsx.ejs +110 -0
- package/hedhog/frontend/app/sessions/[id]/page.tsx.ejs +118 -0
- package/hedhog/frontend/app/sessions/form/page.tsx.ejs +103 -0
- package/hedhog/frontend/app/sessions/page.tsx.ejs +80 -0
- package/hedhog/frontend/app/storage-profiles/form/page.tsx.ejs +100 -0
- package/hedhog/frontend/app/storage-profiles/page.tsx.ejs +80 -0
- package/hedhog/frontend/app/takes/[id]/page.tsx.ejs +143 -0
- package/hedhog/frontend/app/takes/page.tsx.ejs +74 -0
- package/hedhog/table/capture_agent.yaml +48 -0
- package/hedhog/table/edit_composition.yaml +67 -0
- package/hedhog/table/edit_pipeline.yaml +46 -0
- package/hedhog/table/edit_stage_execution.yaml +46 -0
- package/hedhog/table/editor_delivery_package.yaml +63 -0
- package/hedhog/table/ingestion_job.yaml +74 -0
- package/hedhog/table/media_asset.yaml +130 -0
- package/hedhog/table/participant_command_ack.yaml +32 -0
- package/hedhog/table/participant_recording.yaml +67 -0
- package/hedhog/table/production_binding.yaml +34 -0
- package/hedhog/table/production_project.yaml +104 -0
- package/hedhog/table/publication_target.yaml +44 -0
- package/hedhog/table/recorded_file.yaml +74 -0
- package/hedhog/table/recording_command.yaml +72 -0
- package/hedhog/table/recording_incident.yaml +65 -0
- package/hedhog/table/recording_session.yaml +86 -0
- package/hedhog/table/scene.yaml +64 -0
- package/hedhog/table/scene_take.yaml +83 -0
- package/hedhog/table/session_participant.yaml +55 -0
- package/hedhog/table/storage_profile.yaml +75 -0
- package/hedhog/table/sync_marker.yaml +44 -0
- package/package.json +38 -0
- package/src/index.ts +4 -0
- package/src/language/en.json +8 -0
- package/src/language/pt.json +8 -0
- package/src/studio.controller.ts +94 -0
- package/src/studio.module.ts +20 -0
- package/src/studio.service.ts +102 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
Form,
|
|
7
|
+
FormControl,
|
|
8
|
+
FormField,
|
|
9
|
+
FormItem,
|
|
10
|
+
FormLabel,
|
|
11
|
+
FormMessage,
|
|
12
|
+
} from '@/components/ui/form';
|
|
13
|
+
import { Input } from '@/components/ui/input';
|
|
14
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
15
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
16
|
+
import { useForm } from 'react-hook-form';
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
|
|
19
|
+
const schema = z.object({
|
|
20
|
+
title: z.string().min(3),
|
|
21
|
+
description: z.string().optional(),
|
|
22
|
+
projectType: z.string().min(1),
|
|
23
|
+
binding: z.string().optional(),
|
|
24
|
+
owner: z.string().min(1),
|
|
25
|
+
editor: z.string().optional(),
|
|
26
|
+
currentStage: z.string().min(1),
|
|
27
|
+
status: z.string().min(1),
|
|
28
|
+
plannedAt: z.string().optional(),
|
|
29
|
+
thumbnail: z.string().optional(),
|
|
30
|
+
notes: z.string().optional(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export default function StudioProjectFormPage() {
|
|
34
|
+
const form = useForm<z.infer<typeof schema>>({
|
|
35
|
+
resolver: zodResolver(schema),
|
|
36
|
+
defaultValues: {
|
|
37
|
+
title: '',
|
|
38
|
+
description: '',
|
|
39
|
+
projectType: 'course_lesson',
|
|
40
|
+
binding: '',
|
|
41
|
+
owner: '',
|
|
42
|
+
editor: '',
|
|
43
|
+
currentStage: 'preparation',
|
|
44
|
+
status: 'draft',
|
|
45
|
+
plannedAt: '',
|
|
46
|
+
thumbnail: '',
|
|
47
|
+
notes: '',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Page>
|
|
53
|
+
<PageHeader
|
|
54
|
+
title="Formulario de Projeto"
|
|
55
|
+
description="Cadastro estatico para validacao de UX"
|
|
56
|
+
breadcrumbs={[
|
|
57
|
+
{ label: 'Home', href: '/' },
|
|
58
|
+
{ label: 'Studio', href: '/studio' },
|
|
59
|
+
{ label: 'Projeto' },
|
|
60
|
+
]}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<Form {...form}>
|
|
64
|
+
<form
|
|
65
|
+
className="grid gap-4"
|
|
66
|
+
onSubmit={form.handleSubmit(() => undefined)}
|
|
67
|
+
>
|
|
68
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
69
|
+
{[
|
|
70
|
+
['title', 'Titulo', 'Nome do projeto'],
|
|
71
|
+
['projectType', 'Tipo', 'course_lesson | standalone'],
|
|
72
|
+
['binding', 'Vinculo externo', 'lms.lesson #123'],
|
|
73
|
+
['owner', 'Responsavel', 'Usuario responsavel'],
|
|
74
|
+
['editor', 'Editor', 'Usuario editor'],
|
|
75
|
+
['currentStage', 'Etapa atual', 'preparation | recording'],
|
|
76
|
+
['status', 'Status', 'draft | planned | in_editing'],
|
|
77
|
+
['plannedAt', 'Data planejada', '2026-03-20T09:00'],
|
|
78
|
+
['thumbnail', 'Thumbnail', 'ID/URL da imagem'],
|
|
79
|
+
].map(([name, label, placeholder]) => (
|
|
80
|
+
<FormField
|
|
81
|
+
key={name}
|
|
82
|
+
control={form.control}
|
|
83
|
+
name={name as keyof z.infer<typeof schema>}
|
|
84
|
+
render={({ field }) => (
|
|
85
|
+
<FormItem>
|
|
86
|
+
<FormLabel>{label}</FormLabel>
|
|
87
|
+
<FormControl>
|
|
88
|
+
<Input placeholder={placeholder} {...field} />
|
|
89
|
+
</FormControl>
|
|
90
|
+
<FormMessage />
|
|
91
|
+
</FormItem>
|
|
92
|
+
)}
|
|
93
|
+
/>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
<FormField
|
|
97
|
+
control={form.control}
|
|
98
|
+
name="description"
|
|
99
|
+
render={({ field }) => (
|
|
100
|
+
<FormItem>
|
|
101
|
+
<FormLabel>Descricao</FormLabel>
|
|
102
|
+
<FormControl>
|
|
103
|
+
<Textarea placeholder="Resumo do projeto" {...field} />
|
|
104
|
+
</FormControl>
|
|
105
|
+
<FormMessage />
|
|
106
|
+
</FormItem>
|
|
107
|
+
)}
|
|
108
|
+
/>
|
|
109
|
+
<FormField
|
|
110
|
+
control={form.control}
|
|
111
|
+
name="notes"
|
|
112
|
+
render={({ field }) => (
|
|
113
|
+
<FormItem>
|
|
114
|
+
<FormLabel>Observacoes</FormLabel>
|
|
115
|
+
<FormControl>
|
|
116
|
+
<Textarea placeholder="Notas de producao" {...field} />
|
|
117
|
+
</FormControl>
|
|
118
|
+
<FormMessage />
|
|
119
|
+
</FormItem>
|
|
120
|
+
)}
|
|
121
|
+
/>
|
|
122
|
+
<Button type="submit" className="w-full">
|
|
123
|
+
Salvar Projeto
|
|
124
|
+
</Button>
|
|
125
|
+
</form>
|
|
126
|
+
</Form>
|
|
127
|
+
</Page>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Page,
|
|
5
|
+
PageHeader,
|
|
6
|
+
PaginationFooter,
|
|
7
|
+
SearchBar,
|
|
8
|
+
} from '@/components/entity-list';
|
|
9
|
+
import type { PageHeaderProps } from '@/components/entity-list/page-header';
|
|
10
|
+
import { Button } from '@/components/ui/button';
|
|
11
|
+
import {
|
|
12
|
+
Table,
|
|
13
|
+
TableBody,
|
|
14
|
+
TableCell,
|
|
15
|
+
TableHead,
|
|
16
|
+
TableHeader,
|
|
17
|
+
TableRow,
|
|
18
|
+
} from '@/components/ui/table';
|
|
19
|
+
import Link from 'next/link';
|
|
20
|
+
import { useMemo, useState } from 'react';
|
|
21
|
+
import { StudioStatusBadge } from '../_components/studio-status-badge';
|
|
22
|
+
import { STUDIO_PROJECTS } from '../_lib/mocks';
|
|
23
|
+
|
|
24
|
+
export default function StudioProjectsPage() {
|
|
25
|
+
const breadcrumbs: PageHeaderProps['breadcrumbs'] = [
|
|
26
|
+
{ label: 'Home', href: '/' },
|
|
27
|
+
{ label: 'Studio', href: '/studio' },
|
|
28
|
+
{ label: 'Projetos' },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const [search, setSearch] = useState('');
|
|
32
|
+
const [status, setStatus] = useState('all');
|
|
33
|
+
const [type, setType] = useState('all');
|
|
34
|
+
const [stage, setStage] = useState('all');
|
|
35
|
+
const [page, setPage] = useState(1);
|
|
36
|
+
const [pageSize, setPageSize] = useState(10);
|
|
37
|
+
|
|
38
|
+
const filtered = useMemo(() => {
|
|
39
|
+
return STUDIO_PROJECTS.filter((item) => {
|
|
40
|
+
if (status !== 'all' && item.status !== status) return false;
|
|
41
|
+
if (type !== 'all' && item.projectType !== type) return false;
|
|
42
|
+
if (stage !== 'all' && item.currentStage !== stage) return false;
|
|
43
|
+
if (
|
|
44
|
+
search &&
|
|
45
|
+
!item.title.toLowerCase().includes(search.toLowerCase()) &&
|
|
46
|
+
!item.binding.toLowerCase().includes(search.toLowerCase())
|
|
47
|
+
) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
});
|
|
52
|
+
}, [search, stage, status, type]);
|
|
53
|
+
|
|
54
|
+
const paged = filtered.slice((page - 1) * pageSize, page * pageSize);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Page>
|
|
58
|
+
<PageHeader
|
|
59
|
+
title="Projetos"
|
|
60
|
+
description="Gestao de projetos audiovisuais"
|
|
61
|
+
breadcrumbs={breadcrumbs as { label: string; href?: string }[]}
|
|
62
|
+
actions={
|
|
63
|
+
<Button asChild size="sm">
|
|
64
|
+
<Link href="/studio/projects/form">Novo Projeto</Link>
|
|
65
|
+
</Button>
|
|
66
|
+
}
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
<SearchBar
|
|
70
|
+
searchQuery={search}
|
|
71
|
+
onSearchChange={setSearch}
|
|
72
|
+
onSearch={() => setPage(1)}
|
|
73
|
+
placeholder="Buscar por titulo ou vinculo"
|
|
74
|
+
controls={[
|
|
75
|
+
{
|
|
76
|
+
id: 'status',
|
|
77
|
+
type: 'select',
|
|
78
|
+
value: status,
|
|
79
|
+
onChange: setStatus,
|
|
80
|
+
options: [
|
|
81
|
+
{ value: 'all', label: 'Status' },
|
|
82
|
+
{ value: 'draft', label: 'Draft' },
|
|
83
|
+
{ value: 'in_recording', label: 'In recording' },
|
|
84
|
+
{ value: 'in_editing', label: 'In editing' },
|
|
85
|
+
{ value: 'ready_to_publish', label: 'Ready to publish' },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'type',
|
|
90
|
+
type: 'select',
|
|
91
|
+
value: type,
|
|
92
|
+
onChange: setType,
|
|
93
|
+
options: [
|
|
94
|
+
{ value: 'all', label: 'Tipo' },
|
|
95
|
+
{ value: 'course_lesson', label: 'Course lesson' },
|
|
96
|
+
{ value: 'standalone', label: 'Standalone' },
|
|
97
|
+
{ value: 'social_content', label: 'Social content' },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'stage',
|
|
102
|
+
type: 'select',
|
|
103
|
+
value: stage,
|
|
104
|
+
onChange: setStage,
|
|
105
|
+
options: [
|
|
106
|
+
{ value: 'all', label: 'Etapa' },
|
|
107
|
+
{ value: 'preparation', label: 'Preparation' },
|
|
108
|
+
{ value: 'recording', label: 'Recording' },
|
|
109
|
+
{ value: 'rough_cut', label: 'Rough cut' },
|
|
110
|
+
{ value: 'publication', label: 'Publication' },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
]}
|
|
114
|
+
/>
|
|
115
|
+
|
|
116
|
+
<div className="rounded-md border">
|
|
117
|
+
<Table>
|
|
118
|
+
<TableHeader>
|
|
119
|
+
<TableRow>
|
|
120
|
+
<TableHead>Titulo</TableHead>
|
|
121
|
+
<TableHead>Tipo</TableHead>
|
|
122
|
+
<TableHead>Status</TableHead>
|
|
123
|
+
<TableHead>Etapa</TableHead>
|
|
124
|
+
<TableHead>Vinculo</TableHead>
|
|
125
|
+
<TableHead>Responsavel</TableHead>
|
|
126
|
+
<TableHead>Atualizado</TableHead>
|
|
127
|
+
<TableHead className="text-right">Acoes</TableHead>
|
|
128
|
+
</TableRow>
|
|
129
|
+
</TableHeader>
|
|
130
|
+
<TableBody>
|
|
131
|
+
{paged.map((project) => (
|
|
132
|
+
<TableRow key={project.id}>
|
|
133
|
+
<TableCell className="font-medium">{project.title}</TableCell>
|
|
134
|
+
<TableCell>{project.projectType}</TableCell>
|
|
135
|
+
<TableCell>
|
|
136
|
+
<StudioStatusBadge value={project.status} />
|
|
137
|
+
</TableCell>
|
|
138
|
+
<TableCell>
|
|
139
|
+
<StudioStatusBadge value={project.currentStage} />
|
|
140
|
+
</TableCell>
|
|
141
|
+
<TableCell>{project.binding}</TableCell>
|
|
142
|
+
<TableCell>{project.owner}</TableCell>
|
|
143
|
+
<TableCell>
|
|
144
|
+
{new Date(project.updatedAt).toLocaleString('pt-BR')}
|
|
145
|
+
</TableCell>
|
|
146
|
+
<TableCell className="text-right">
|
|
147
|
+
<Button asChild variant="outline" size="sm">
|
|
148
|
+
<Link href={`/studio/projects/${project.id}`}>Abrir</Link>
|
|
149
|
+
</Button>
|
|
150
|
+
</TableCell>
|
|
151
|
+
</TableRow>
|
|
152
|
+
))}
|
|
153
|
+
</TableBody>
|
|
154
|
+
</Table>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<PaginationFooter
|
|
158
|
+
currentPage={page}
|
|
159
|
+
pageSize={pageSize}
|
|
160
|
+
totalItems={filtered.length}
|
|
161
|
+
onPageChange={setPage}
|
|
162
|
+
onPageSizeChange={(value) => {
|
|
163
|
+
setPageSize(value);
|
|
164
|
+
setPage(1);
|
|
165
|
+
}}
|
|
166
|
+
/>
|
|
167
|
+
</Page>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import {
|
|
5
|
+
Table,
|
|
6
|
+
TableBody,
|
|
7
|
+
TableCell,
|
|
8
|
+
TableHead,
|
|
9
|
+
TableHeader,
|
|
10
|
+
TableRow,
|
|
11
|
+
} from '@/components/ui/table';
|
|
12
|
+
import { Button } from '@/components/ui/button';
|
|
13
|
+
import { STUDIO_PUBLICATION_TARGETS } from '../_lib/mocks';
|
|
14
|
+
import { StudioStatusBadge } from '../_components/studio-status-badge';
|
|
15
|
+
|
|
16
|
+
export default function StudioPublicationPage() {
|
|
17
|
+
return (
|
|
18
|
+
<Page>
|
|
19
|
+
<PageHeader
|
|
20
|
+
title="Publicacao"
|
|
21
|
+
description="Destinos e estado de publicacao"
|
|
22
|
+
breadcrumbs={[
|
|
23
|
+
{ label: 'Home', href: '/' },
|
|
24
|
+
{ label: 'Studio', href: '/studio' },
|
|
25
|
+
{ label: 'Publicacao' },
|
|
26
|
+
]}
|
|
27
|
+
/>
|
|
28
|
+
<div className="rounded-md border">
|
|
29
|
+
<Table>
|
|
30
|
+
<TableHeader>
|
|
31
|
+
<TableRow>
|
|
32
|
+
<TableHead>Destino</TableHead>
|
|
33
|
+
<TableHead>Status</TableHead>
|
|
34
|
+
<TableHead>Agendamento</TableHead>
|
|
35
|
+
<TableHead>URL</TableHead>
|
|
36
|
+
<TableHead className="text-right">Acoes</TableHead>
|
|
37
|
+
</TableRow>
|
|
38
|
+
</TableHeader>
|
|
39
|
+
<TableBody>
|
|
40
|
+
{STUDIO_PUBLICATION_TARGETS.map((target) => (
|
|
41
|
+
<TableRow key={target.id}>
|
|
42
|
+
<TableCell>{target.targetType}</TableCell>
|
|
43
|
+
<TableCell>
|
|
44
|
+
<StudioStatusBadge value={target.status} />
|
|
45
|
+
</TableCell>
|
|
46
|
+
<TableCell>
|
|
47
|
+
{target.scheduledAt
|
|
48
|
+
? new Date(target.scheduledAt).toLocaleString('pt-BR')
|
|
49
|
+
: '-'}
|
|
50
|
+
</TableCell>
|
|
51
|
+
<TableCell>{target.publishedUrl || '-'}</TableCell>
|
|
52
|
+
<TableCell className="text-right">
|
|
53
|
+
<Button size="sm">Publicar</Button>
|
|
54
|
+
</TableCell>
|
|
55
|
+
</TableRow>
|
|
56
|
+
))}
|
|
57
|
+
</TableBody>
|
|
58
|
+
</Table>
|
|
59
|
+
</div>
|
|
60
|
+
</Page>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
Form,
|
|
7
|
+
FormControl,
|
|
8
|
+
FormField,
|
|
9
|
+
FormItem,
|
|
10
|
+
FormLabel,
|
|
11
|
+
FormMessage,
|
|
12
|
+
} from '@/components/ui/form';
|
|
13
|
+
import { Input } from '@/components/ui/input';
|
|
14
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
15
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
16
|
+
import { useForm } from 'react-hook-form';
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
|
|
19
|
+
const schema = z.object({
|
|
20
|
+
name: z.string().min(2),
|
|
21
|
+
description: z.string().optional(),
|
|
22
|
+
scriptExcerpt: z.string().optional(),
|
|
23
|
+
sequenceOrder: z.string().min(1),
|
|
24
|
+
estimatedDuration: z.string().optional(),
|
|
25
|
+
status: z.string().min(1),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export default function StudioSceneFormPage() {
|
|
29
|
+
const form = useForm<z.infer<typeof schema>>({
|
|
30
|
+
resolver: zodResolver(schema),
|
|
31
|
+
defaultValues: {
|
|
32
|
+
name: '',
|
|
33
|
+
description: '',
|
|
34
|
+
scriptExcerpt: '',
|
|
35
|
+
sequenceOrder: '1',
|
|
36
|
+
estimatedDuration: '',
|
|
37
|
+
status: 'pending',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Page>
|
|
43
|
+
<PageHeader
|
|
44
|
+
title="Formulario de Cena"
|
|
45
|
+
description="Cadastro estatico de cena"
|
|
46
|
+
breadcrumbs={[
|
|
47
|
+
{ label: 'Home', href: '/' },
|
|
48
|
+
{ label: 'Studio', href: '/studio' },
|
|
49
|
+
{ label: 'Cena' },
|
|
50
|
+
]}
|
|
51
|
+
/>
|
|
52
|
+
<Form {...form}>
|
|
53
|
+
<form
|
|
54
|
+
className="grid gap-4"
|
|
55
|
+
onSubmit={form.handleSubmit(() => undefined)}
|
|
56
|
+
>
|
|
57
|
+
{[
|
|
58
|
+
['name', 'Nome', 'Cena de abertura'],
|
|
59
|
+
['sequenceOrder', 'Ordem', '1'],
|
|
60
|
+
['estimatedDuration', 'Duracao estimada (s)', '120'],
|
|
61
|
+
['status', 'Status', 'pending | recorded'],
|
|
62
|
+
].map(([name, label, placeholder]) => (
|
|
63
|
+
<FormField
|
|
64
|
+
key={name}
|
|
65
|
+
control={form.control}
|
|
66
|
+
name={name as keyof z.infer<typeof schema>}
|
|
67
|
+
render={({ field }) => (
|
|
68
|
+
<FormItem>
|
|
69
|
+
<FormLabel>{label}</FormLabel>
|
|
70
|
+
<FormControl>
|
|
71
|
+
<Input placeholder={placeholder} {...field} />
|
|
72
|
+
</FormControl>
|
|
73
|
+
<FormMessage />
|
|
74
|
+
</FormItem>
|
|
75
|
+
)}
|
|
76
|
+
/>
|
|
77
|
+
))}
|
|
78
|
+
|
|
79
|
+
<FormField
|
|
80
|
+
control={form.control}
|
|
81
|
+
name="description"
|
|
82
|
+
render={({ field }) => (
|
|
83
|
+
<FormItem>
|
|
84
|
+
<FormLabel>Descricao</FormLabel>
|
|
85
|
+
<FormControl>
|
|
86
|
+
<Textarea placeholder="Descricao da cena" {...field} />
|
|
87
|
+
</FormControl>
|
|
88
|
+
</FormItem>
|
|
89
|
+
)}
|
|
90
|
+
/>
|
|
91
|
+
<FormField
|
|
92
|
+
control={form.control}
|
|
93
|
+
name="scriptExcerpt"
|
|
94
|
+
render={({ field }) => (
|
|
95
|
+
<FormItem>
|
|
96
|
+
<FormLabel>Trecho do script</FormLabel>
|
|
97
|
+
<FormControl>
|
|
98
|
+
<Textarea placeholder="Fal falada ou orientacao" {...field} />
|
|
99
|
+
</FormControl>
|
|
100
|
+
</FormItem>
|
|
101
|
+
)}
|
|
102
|
+
/>
|
|
103
|
+
<Button type="submit" className="w-full">
|
|
104
|
+
Salvar Cena
|
|
105
|
+
</Button>
|
|
106
|
+
</form>
|
|
107
|
+
</Form>
|
|
108
|
+
</Page>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import type { PageHeaderProps } from '@/components/entity-list/page-header';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
7
|
+
import { useParams } from 'next/navigation';
|
|
8
|
+
import { StudioStatusBadge } from '../../_components/studio-status-badge';
|
|
9
|
+
import { STUDIO_SESSIONS } from '../../_lib/mocks';
|
|
10
|
+
|
|
11
|
+
const participantStatus = [
|
|
12
|
+
{ name: 'Ana Lima', status: 'recording', device: 'desktop_app ok' },
|
|
13
|
+
{ name: 'Bruno Costa', status: 'ready', device: 'desktop_app ok' },
|
|
14
|
+
{ name: 'Diego Nunes', status: 'recording', device: 'desktop_app warning' },
|
|
15
|
+
{ name: 'Eva Souza', status: 'connected', device: 'browser ok' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export default function StudioSessionDetailPage() {
|
|
19
|
+
const params = useParams<{ id: string }>();
|
|
20
|
+
const session = STUDIO_SESSIONS.find((item) => item.id === Number(params.id));
|
|
21
|
+
|
|
22
|
+
if (!session) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const currentSession = session!;
|
|
27
|
+
|
|
28
|
+
const breadcrumbs: PageHeaderProps['breadcrumbs'] = [
|
|
29
|
+
{ label: 'Home', href: '/' },
|
|
30
|
+
{ label: 'Studio', href: '/studio' },
|
|
31
|
+
{ label: 'Sessoes', href: '/studio/sessions' },
|
|
32
|
+
{ label: `Sessao #${currentSession.id}` },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Page>
|
|
37
|
+
<PageHeader
|
|
38
|
+
title={currentSession.title}
|
|
39
|
+
description="Monitoramento de sessao ativa"
|
|
40
|
+
breadcrumbs={breadcrumbs as { label: string; href?: string }[]}
|
|
41
|
+
/>
|
|
42
|
+
|
|
43
|
+
<div className="grid gap-4 lg:grid-cols-3">
|
|
44
|
+
<Card>
|
|
45
|
+
<CardHeader>
|
|
46
|
+
<CardTitle className="text-base">Estado da sessao</CardTitle>
|
|
47
|
+
</CardHeader>
|
|
48
|
+
<CardContent className="space-y-2 text-sm">
|
|
49
|
+
<div>
|
|
50
|
+
Status: <StudioStatusBadge value={currentSession.status} />
|
|
51
|
+
</div>
|
|
52
|
+
<div>Room: {currentSession.roomKey}</div>
|
|
53
|
+
<div>Cena atual: Demonstracao em tela</div>
|
|
54
|
+
<div>Host: {currentSession.host}</div>
|
|
55
|
+
</CardContent>
|
|
56
|
+
</Card>
|
|
57
|
+
<Card className="lg:col-span-2">
|
|
58
|
+
<CardHeader>
|
|
59
|
+
<CardTitle className="text-base">Comandos</CardTitle>
|
|
60
|
+
</CardHeader>
|
|
61
|
+
<CardContent className="grid gap-2 sm:grid-cols-2 lg:grid-cols-5">
|
|
62
|
+
<Button variant="outline">Armar</Button>
|
|
63
|
+
<Button>Iniciar gravacao</Button>
|
|
64
|
+
<Button variant="secondary">Parar</Button>
|
|
65
|
+
<Button variant="destructive">Cancelar take</Button>
|
|
66
|
+
<Button variant="outline">Proximo take</Button>
|
|
67
|
+
</CardContent>
|
|
68
|
+
</Card>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div className="grid gap-4 lg:grid-cols-2">
|
|
72
|
+
<Card>
|
|
73
|
+
<CardHeader>
|
|
74
|
+
<CardTitle className="text-base">
|
|
75
|
+
Participantes conectados
|
|
76
|
+
</CardTitle>
|
|
77
|
+
</CardHeader>
|
|
78
|
+
<CardContent className="space-y-2 text-sm">
|
|
79
|
+
{participantStatus.map((participant) => (
|
|
80
|
+
<div
|
|
81
|
+
key={participant.name}
|
|
82
|
+
className="flex items-center justify-between rounded-md border p-2"
|
|
83
|
+
>
|
|
84
|
+
<div>
|
|
85
|
+
<div className="font-medium">{participant.name}</div>
|
|
86
|
+
<div className="text-muted-foreground text-xs">
|
|
87
|
+
{participant.device}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
<StudioStatusBadge value={participant.status} />
|
|
91
|
+
</div>
|
|
92
|
+
))}
|
|
93
|
+
</CardContent>
|
|
94
|
+
</Card>
|
|
95
|
+
<Card>
|
|
96
|
+
<CardHeader>
|
|
97
|
+
<CardTitle className="text-base">Timeline de eventos</CardTitle>
|
|
98
|
+
</CardHeader>
|
|
99
|
+
<CardContent className="space-y-2 text-sm">
|
|
100
|
+
<div className="rounded-md border p-2">18:00 - Sessao iniciou</div>
|
|
101
|
+
<div className="rounded-md border p-2">
|
|
102
|
+
18:02 - Command arm emitido
|
|
103
|
+
</div>
|
|
104
|
+
<div className="rounded-md border p-2">
|
|
105
|
+
18:03 - Command start emitido
|
|
106
|
+
</div>
|
|
107
|
+
<div className="rounded-md border p-2">
|
|
108
|
+
18:08 - Incident delayed_stop detectado
|
|
109
|
+
</div>
|
|
110
|
+
<div className="rounded-md border p-2">
|
|
111
|
+
18:10 - Command stop emitido
|
|
112
|
+
</div>
|
|
113
|
+
</CardContent>
|
|
114
|
+
</Card>
|
|
115
|
+
</div>
|
|
116
|
+
</Page>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
Form,
|
|
7
|
+
FormControl,
|
|
8
|
+
FormField,
|
|
9
|
+
FormItem,
|
|
10
|
+
FormLabel,
|
|
11
|
+
FormMessage,
|
|
12
|
+
} from '@/components/ui/form';
|
|
13
|
+
import { Input } from '@/components/ui/input';
|
|
14
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
15
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
16
|
+
import { useForm } from 'react-hook-form';
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
|
|
19
|
+
const schema = z.object({
|
|
20
|
+
title: z.string().min(3),
|
|
21
|
+
description: z.string().optional(),
|
|
22
|
+
sessionType: z.string().min(1),
|
|
23
|
+
roomKey: z.string().min(1),
|
|
24
|
+
host: z.string().min(1),
|
|
25
|
+
status: z.string().min(1),
|
|
26
|
+
syncMode: z.string().min(1),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export default function StudioSessionFormPage() {
|
|
30
|
+
const form = useForm<z.infer<typeof schema>>({
|
|
31
|
+
resolver: zodResolver(schema),
|
|
32
|
+
defaultValues: {
|
|
33
|
+
title: '',
|
|
34
|
+
description: '',
|
|
35
|
+
sessionType: 'remote_collaborative',
|
|
36
|
+
roomKey: '',
|
|
37
|
+
host: '',
|
|
38
|
+
status: 'idle',
|
|
39
|
+
syncMode: 'server_authoritative',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Page>
|
|
45
|
+
<PageHeader
|
|
46
|
+
title="Formulario de Sessao"
|
|
47
|
+
description="Cadastro estatico de sessao de gravacao"
|
|
48
|
+
breadcrumbs={[
|
|
49
|
+
{ label: 'Home', href: '/' },
|
|
50
|
+
{ label: 'Studio', href: '/studio' },
|
|
51
|
+
{ label: 'Sessao' },
|
|
52
|
+
]}
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
<Form {...form}>
|
|
56
|
+
<form
|
|
57
|
+
className="grid gap-4"
|
|
58
|
+
onSubmit={form.handleSubmit(() => undefined)}
|
|
59
|
+
>
|
|
60
|
+
{[
|
|
61
|
+
['title', 'Titulo', 'Sessao principal do projeto X'],
|
|
62
|
+
['sessionType', 'Tipo', 'local | remote_collaborative | hybrid'],
|
|
63
|
+
['roomKey', 'Room key', 'studio-room-abc'],
|
|
64
|
+
['host', 'Host', 'Usuario host'],
|
|
65
|
+
['status', 'Status', 'idle | ready | recording'],
|
|
66
|
+
['syncMode', 'Modo de sincronizacao', 'server_authoritative'],
|
|
67
|
+
].map(([name, label, placeholder]) => (
|
|
68
|
+
<FormField
|
|
69
|
+
key={name}
|
|
70
|
+
control={form.control}
|
|
71
|
+
name={name as keyof z.infer<typeof schema>}
|
|
72
|
+
render={({ field }) => (
|
|
73
|
+
<FormItem>
|
|
74
|
+
<FormLabel>{label}</FormLabel>
|
|
75
|
+
<FormControl>
|
|
76
|
+
<Input placeholder={placeholder} {...field} />
|
|
77
|
+
</FormControl>
|
|
78
|
+
<FormMessage />
|
|
79
|
+
</FormItem>
|
|
80
|
+
)}
|
|
81
|
+
/>
|
|
82
|
+
))}
|
|
83
|
+
<FormField
|
|
84
|
+
control={form.control}
|
|
85
|
+
name="description"
|
|
86
|
+
render={({ field }) => (
|
|
87
|
+
<FormItem>
|
|
88
|
+
<FormLabel>Descricao</FormLabel>
|
|
89
|
+
<FormControl>
|
|
90
|
+
<Textarea placeholder="Contexto da sessao" {...field} />
|
|
91
|
+
</FormControl>
|
|
92
|
+
<FormMessage />
|
|
93
|
+
</FormItem>
|
|
94
|
+
)}
|
|
95
|
+
/>
|
|
96
|
+
<Button type="submit" className="w-full">
|
|
97
|
+
Salvar Sessao
|
|
98
|
+
</Button>
|
|
99
|
+
</form>
|
|
100
|
+
</Form>
|
|
101
|
+
</Page>
|
|
102
|
+
);
|
|
103
|
+
}
|