@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.
Files changed (68) hide show
  1. package/dist/index.d.ts +4 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +20 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/studio.controller.d.ts +79 -0
  6. package/dist/studio.controller.d.ts.map +1 -0
  7. package/dist/studio.controller.js +186 -0
  8. package/dist/studio.controller.js.map +1 -0
  9. package/dist/studio.module.d.ts +3 -0
  10. package/dist/studio.module.d.ts.map +1 -0
  11. package/dist/studio.module.js +33 -0
  12. package/dist/studio.module.js.map +1 -0
  13. package/dist/studio.service.d.ts +76 -0
  14. package/dist/studio.service.d.ts.map +1 -0
  15. package/dist/studio.service.js +98 -0
  16. package/dist/studio.service.js.map +1 -0
  17. package/hedhog/data/menu.yaml +114 -0
  18. package/hedhog/data/role.yaml +7 -0
  19. package/hedhog/data/route.yaml +152 -0
  20. package/hedhog/data/setting_group.yaml +8 -0
  21. package/hedhog/frontend/app/_components/studio-status-badge.tsx.ejs +14 -0
  22. package/hedhog/frontend/app/_lib/mocks.ts.ejs +209 -0
  23. package/hedhog/frontend/app/_lib/status.ts.ejs +38 -0
  24. package/hedhog/frontend/app/_lib/types.ts.ejs +148 -0
  25. package/hedhog/frontend/app/assets/page.tsx.ejs +117 -0
  26. package/hedhog/frontend/app/editing/page.tsx.ejs +55 -0
  27. package/hedhog/frontend/app/incidents/page.tsx.ejs +59 -0
  28. package/hedhog/frontend/app/page.tsx.ejs +207 -0
  29. package/hedhog/frontend/app/projects/[id]/page.tsx.ejs +323 -0
  30. package/hedhog/frontend/app/projects/form/page.tsx.ejs +129 -0
  31. package/hedhog/frontend/app/projects/page.tsx.ejs +169 -0
  32. package/hedhog/frontend/app/publication/page.tsx.ejs +62 -0
  33. package/hedhog/frontend/app/scenes/form/page.tsx.ejs +110 -0
  34. package/hedhog/frontend/app/sessions/[id]/page.tsx.ejs +118 -0
  35. package/hedhog/frontend/app/sessions/form/page.tsx.ejs +103 -0
  36. package/hedhog/frontend/app/sessions/page.tsx.ejs +80 -0
  37. package/hedhog/frontend/app/storage-profiles/form/page.tsx.ejs +100 -0
  38. package/hedhog/frontend/app/storage-profiles/page.tsx.ejs +80 -0
  39. package/hedhog/frontend/app/takes/[id]/page.tsx.ejs +143 -0
  40. package/hedhog/frontend/app/takes/page.tsx.ejs +74 -0
  41. package/hedhog/table/capture_agent.yaml +48 -0
  42. package/hedhog/table/edit_composition.yaml +67 -0
  43. package/hedhog/table/edit_pipeline.yaml +46 -0
  44. package/hedhog/table/edit_stage_execution.yaml +46 -0
  45. package/hedhog/table/editor_delivery_package.yaml +63 -0
  46. package/hedhog/table/ingestion_job.yaml +74 -0
  47. package/hedhog/table/media_asset.yaml +130 -0
  48. package/hedhog/table/participant_command_ack.yaml +32 -0
  49. package/hedhog/table/participant_recording.yaml +67 -0
  50. package/hedhog/table/production_binding.yaml +34 -0
  51. package/hedhog/table/production_project.yaml +104 -0
  52. package/hedhog/table/publication_target.yaml +44 -0
  53. package/hedhog/table/recorded_file.yaml +74 -0
  54. package/hedhog/table/recording_command.yaml +72 -0
  55. package/hedhog/table/recording_incident.yaml +65 -0
  56. package/hedhog/table/recording_session.yaml +86 -0
  57. package/hedhog/table/scene.yaml +64 -0
  58. package/hedhog/table/scene_take.yaml +83 -0
  59. package/hedhog/table/session_participant.yaml +55 -0
  60. package/hedhog/table/storage_profile.yaml +75 -0
  61. package/hedhog/table/sync_marker.yaml +44 -0
  62. package/package.json +38 -0
  63. package/src/index.ts +4 -0
  64. package/src/language/en.json +8 -0
  65. package/src/language/pt.json +8 -0
  66. package/src/studio.controller.ts +94 -0
  67. package/src/studio.module.ts +20 -0
  68. package/src/studio.service.ts +102 -0
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
5
+
6
+ export default function StudioEditingPage() {
7
+ return (
8
+ <Page>
9
+ <PageHeader
10
+ title="Edicao"
11
+ description="Pipeline e execucao de etapas de edicao"
12
+ breadcrumbs={[
13
+ { label: 'Home', href: '/' },
14
+ { label: 'Studio', href: '/studio' },
15
+ { label: 'Edicao' },
16
+ ]}
17
+ />
18
+ <div className="grid gap-4 lg:grid-cols-3">
19
+ <Card>
20
+ <CardHeader>
21
+ <CardTitle className="text-base">Pipeline atual</CardTitle>
22
+ </CardHeader>
23
+ <CardContent className="space-y-2 text-sm">
24
+ <div>Preparation • completed</div>
25
+ <div>Recording • completed</div>
26
+ <div>Rough cut • in_progress</div>
27
+ <div>Cleanup • pending</div>
28
+ <div>Finalization • pending</div>
29
+ <div>Publication • pending</div>
30
+ </CardContent>
31
+ </Card>
32
+ <Card>
33
+ <CardHeader>
34
+ <CardTitle className="text-base">Composicao base</CardTitle>
35
+ </CardHeader>
36
+ <CardContent className="space-y-2 text-sm">
37
+ <div>Tipo: multi_participant</div>
38
+ <div>Fonte primaria: microphone_audio</div>
39
+ <div>Status: in_editing</div>
40
+ </CardContent>
41
+ </Card>
42
+ <Card>
43
+ <CardHeader>
44
+ <CardTitle className="text-base">Pacotes</CardTitle>
45
+ </CardHeader>
46
+ <CardContent className="space-y-2 text-sm">
47
+ <div>raw_take_bundle • available</div>
48
+ <div>editing_bundle • generating</div>
49
+ <div>proxy_bundle • pending</div>
50
+ </CardContent>
51
+ </Card>
52
+ </div>
53
+ </Page>
54
+ );
55
+ }
@@ -0,0 +1,59 @@
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 { STUDIO_INCIDENTS } from '../_lib/mocks';
13
+ import { StudioStatusBadge } from '../_components/studio-status-badge';
14
+
15
+ export default function StudioIncidentsPage() {
16
+ return (
17
+ <Page>
18
+ <PageHeader
19
+ title="Incidentes"
20
+ description="Falhas tecnicas e acompanhamento de resolucao"
21
+ breadcrumbs={[
22
+ { label: 'Home', href: '/' },
23
+ { label: 'Studio', href: '/studio' },
24
+ { label: 'Incidentes' },
25
+ ]}
26
+ />
27
+ <div className="rounded-md border">
28
+ <Table>
29
+ <TableHeader>
30
+ <TableRow>
31
+ <TableHead>Tipo</TableHead>
32
+ <TableHead>Severidade</TableHead>
33
+ <TableHead>Alvo</TableHead>
34
+ <TableHead>Status</TableHead>
35
+ <TableHead>Detectado em</TableHead>
36
+ </TableRow>
37
+ </TableHeader>
38
+ <TableBody>
39
+ {STUDIO_INCIDENTS.map((incident) => (
40
+ <TableRow key={incident.id}>
41
+ <TableCell>{incident.type}</TableCell>
42
+ <TableCell>
43
+ <StudioStatusBadge value={incident.severity} />
44
+ </TableCell>
45
+ <TableCell>{incident.target}</TableCell>
46
+ <TableCell>
47
+ <StudioStatusBadge value={incident.status} />
48
+ </TableCell>
49
+ <TableCell>
50
+ {new Date(incident.detectedAt).toLocaleString('pt-BR')}
51
+ </TableCell>
52
+ </TableRow>
53
+ ))}
54
+ </TableBody>
55
+ </Table>
56
+ </div>
57
+ </Page>
58
+ );
59
+ }
@@ -0,0 +1,207 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Page,
5
+ PageHeader,
6
+ StatsCards,
7
+ type StatCardConfig,
8
+ } from '@/components/entity-list';
9
+ import type { PageHeaderProps } from '@/components/entity-list/page-header';
10
+ import { Button } from '@/components/ui/button';
11
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
12
+ import {
13
+ Table,
14
+ TableBody,
15
+ TableCell,
16
+ TableHead,
17
+ TableHeader,
18
+ TableRow,
19
+ } from '@/components/ui/table';
20
+ import {
21
+ AlertTriangle,
22
+ Clapperboard,
23
+ Clock3,
24
+ Film,
25
+ UploadCloud,
26
+ Video,
27
+ } from 'lucide-react';
28
+ import Link from 'next/link';
29
+ import { StudioStatusBadge } from './_components/studio-status-badge';
30
+ import {
31
+ STUDIO_INCIDENTS,
32
+ STUDIO_PROJECTS,
33
+ STUDIO_SESSIONS,
34
+ STUDIO_TAKES,
35
+ } from './_lib/mocks';
36
+
37
+ export default function StudioDashboardPage() {
38
+ const breadcrumbs: PageHeaderProps['breadcrumbs'] = [
39
+ { label: 'Home', href: '/' },
40
+ { label: 'Studio' },
41
+ ];
42
+
43
+ const stats: StatCardConfig[] = [
44
+ {
45
+ title: 'Projetos totais',
46
+ value: STUDIO_PROJECTS.length,
47
+ icon: <Clapperboard className="size-5" />,
48
+ iconBgColor: 'bg-cyan-50',
49
+ iconColor: 'text-cyan-700',
50
+ },
51
+ {
52
+ title: 'Em gravacao',
53
+ value: STUDIO_PROJECTS.filter((item) => item.status === 'in_recording')
54
+ .length,
55
+ icon: <Video className="size-5" />,
56
+ iconBgColor: 'bg-orange-50',
57
+ iconColor: 'text-orange-700',
58
+ },
59
+ {
60
+ title: 'Em edicao',
61
+ value: STUDIO_PROJECTS.filter((item) => item.status === 'in_editing')
62
+ .length,
63
+ icon: <Film className="size-5" />,
64
+ iconBgColor: 'bg-violet-50',
65
+ iconColor: 'text-violet-700',
66
+ },
67
+ {
68
+ title: 'Takes pendentes',
69
+ value: STUDIO_TAKES.filter((item) => item.status === 'partial').length,
70
+ icon: <UploadCloud className="size-5" />,
71
+ iconBgColor: 'bg-amber-50',
72
+ iconColor: 'text-amber-700',
73
+ },
74
+ ];
75
+
76
+ return (
77
+ <Page>
78
+ <PageHeader
79
+ title="Studio"
80
+ description="Visao operacional de producao audiovisual"
81
+ breadcrumbs={breadcrumbs as { label: string; href?: string }[]}
82
+ actions={
83
+ <Button asChild size="sm">
84
+ <Link href="/studio/projects/form">Novo Projeto</Link>
85
+ </Button>
86
+ }
87
+ />
88
+
89
+ <StatsCards stats={stats} />
90
+
91
+ <div className="grid gap-4 lg:grid-cols-2">
92
+ <Card>
93
+ <CardHeader>
94
+ <CardTitle className="text-base">Projetos recentes</CardTitle>
95
+ </CardHeader>
96
+ <CardContent>
97
+ <Table>
98
+ <TableHeader>
99
+ <TableRow>
100
+ <TableHead>Titulo</TableHead>
101
+ <TableHead>Status</TableHead>
102
+ <TableHead>Responsavel</TableHead>
103
+ </TableRow>
104
+ </TableHeader>
105
+ <TableBody>
106
+ {STUDIO_PROJECTS.map((project) => (
107
+ <TableRow key={project.id}>
108
+ <TableCell>
109
+ <Link
110
+ href={`/studio/projects/${project.id}`}
111
+ className="hover:underline"
112
+ >
113
+ {project.title}
114
+ </Link>
115
+ </TableCell>
116
+ <TableCell>
117
+ <StudioStatusBadge value={project.status} />
118
+ </TableCell>
119
+ <TableCell>{project.owner}</TableCell>
120
+ </TableRow>
121
+ ))}
122
+ </TableBody>
123
+ </Table>
124
+ </CardContent>
125
+ </Card>
126
+
127
+ <Card>
128
+ <CardHeader>
129
+ <CardTitle className="text-base">Sessoes em andamento</CardTitle>
130
+ </CardHeader>
131
+ <CardContent className="space-y-3">
132
+ {STUDIO_SESSIONS.map((session) => (
133
+ <div
134
+ key={session.id}
135
+ className="flex items-center justify-between rounded-md border p-3"
136
+ >
137
+ <div>
138
+ <div className="font-medium">{session.title}</div>
139
+ <div className="text-muted-foreground text-xs">
140
+ Room {session.roomKey}
141
+ </div>
142
+ </div>
143
+ <StudioStatusBadge value={session.status} />
144
+ </div>
145
+ ))}
146
+ </CardContent>
147
+ </Card>
148
+ </div>
149
+
150
+ <div className="grid gap-4 lg:grid-cols-3">
151
+ <Card className="lg:col-span-2">
152
+ <CardHeader>
153
+ <CardTitle className="text-base">Incidentes recentes</CardTitle>
154
+ </CardHeader>
155
+ <CardContent className="space-y-3">
156
+ {STUDIO_INCIDENTS.map((incident) => (
157
+ <div
158
+ key={incident.id}
159
+ className="flex items-center justify-between rounded-md border p-3"
160
+ >
161
+ <div className="flex items-start gap-3">
162
+ <AlertTriangle className="mt-0.5 size-4 text-red-600" />
163
+ <div>
164
+ <div className="font-medium">{incident.type}</div>
165
+ <div className="text-muted-foreground text-xs">
166
+ {incident.target}
167
+ </div>
168
+ </div>
169
+ </div>
170
+ <StudioStatusBadge value={incident.severity} />
171
+ </div>
172
+ ))}
173
+ </CardContent>
174
+ </Card>
175
+
176
+ <Card>
177
+ <CardHeader>
178
+ <CardTitle className="text-base">Atalhos</CardTitle>
179
+ </CardHeader>
180
+ <CardContent className="grid gap-2 text-sm">
181
+ <Link
182
+ href="/studio/projects"
183
+ className="rounded-md border px-3 py-2 hover:bg-muted"
184
+ >
185
+ Ir para projetos
186
+ </Link>
187
+ <Link
188
+ href="/studio/sessions"
189
+ className="rounded-md border px-3 py-2 hover:bg-muted"
190
+ >
191
+ Monitorar sessoes
192
+ </Link>
193
+ <Link
194
+ href="/studio/storage-profiles"
195
+ className="rounded-md border px-3 py-2 hover:bg-muted"
196
+ >
197
+ Gerenciar storage
198
+ </Link>
199
+ <div className="text-muted-foreground flex items-center gap-2 pt-2 text-xs">
200
+ <Clock3 className="size-3" /> Ultima sincronizacao agora
201
+ </div>
202
+ </CardContent>
203
+ </Card>
204
+ </div>
205
+ </Page>
206
+ );
207
+ }
@@ -0,0 +1,323 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import type { PageHeaderProps } from '@/components/entity-list/page-header';
5
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
6
+ import {
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ } from '@/components/ui/table';
14
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
15
+ import { useParams } from 'next/navigation';
16
+ import { StudioStatusBadge } from '../../_components/studio-status-badge';
17
+ import {
18
+ STUDIO_ASSETS,
19
+ STUDIO_INCIDENTS,
20
+ STUDIO_PROJECTS,
21
+ STUDIO_PUBLICATION_TARGETS,
22
+ STUDIO_SCENES,
23
+ STUDIO_SESSIONS,
24
+ STUDIO_TAKES,
25
+ } from '../../_lib/mocks';
26
+
27
+ export default function StudioProjectDetailPage() {
28
+ const params = useParams<{ id: string }>();
29
+ const projectId = Number(params.id);
30
+ const project = STUDIO_PROJECTS.find((item) => item.id === projectId);
31
+
32
+ if (!project) {
33
+ return null;
34
+ }
35
+
36
+ const currentProject = project!;
37
+
38
+ const breadcrumbs: PageHeaderProps['breadcrumbs'] = [
39
+ { label: 'Home', href: '/' },
40
+ { label: 'Studio', href: '/studio' },
41
+ { label: 'Projetos', href: '/studio/projects' },
42
+ { label: `Projeto #${currentProject.id}` },
43
+ ];
44
+
45
+ const scenes = STUDIO_SCENES.filter(
46
+ (item) => item.projectId === currentProject.id
47
+ );
48
+ const sessions = STUDIO_SESSIONS.filter(
49
+ (item) => item.projectId === currentProject.id
50
+ );
51
+ const takes = STUDIO_TAKES.filter(
52
+ (item) => item.projectId === currentProject.id
53
+ );
54
+ const incidents = STUDIO_INCIDENTS.filter(
55
+ (item) => item.projectId === currentProject.id
56
+ );
57
+ const publicationTargets = STUDIO_PUBLICATION_TARGETS.filter(
58
+ (item) => item.projectId === currentProject.id
59
+ );
60
+
61
+ return (
62
+ <Page>
63
+ <PageHeader
64
+ title={currentProject.title}
65
+ description="Painel central do projeto de producao"
66
+ breadcrumbs={breadcrumbs as { label: string; href?: string }[]}
67
+ />
68
+
69
+ <Tabs defaultValue="overview" className="space-y-4">
70
+ <TabsList className="grid w-full grid-cols-4 lg:grid-cols-8">
71
+ <TabsTrigger value="overview">Visao geral</TabsTrigger>
72
+ <TabsTrigger value="scenes">Cenas</TabsTrigger>
73
+ <TabsTrigger value="sessions">Sessoes</TabsTrigger>
74
+ <TabsTrigger value="takes">Takes</TabsTrigger>
75
+ <TabsTrigger value="assets">Arquivos</TabsTrigger>
76
+ <TabsTrigger value="editing">Edicao</TabsTrigger>
77
+ <TabsTrigger value="publication">Publicacao</TabsTrigger>
78
+ <TabsTrigger value="incidents">Incidentes</TabsTrigger>
79
+ </TabsList>
80
+
81
+ <TabsContent value="overview" className="grid gap-4 md:grid-cols-2">
82
+ <Card>
83
+ <CardHeader>
84
+ <CardTitle className="text-base">Resumo</CardTitle>
85
+ </CardHeader>
86
+ <CardContent className="space-y-2 text-sm">
87
+ <div>Tipo: {currentProject.projectType}</div>
88
+ <div>
89
+ Status: <StudioStatusBadge value={currentProject.status} />
90
+ </div>
91
+ <div>
92
+ Etapa: <StudioStatusBadge value={currentProject.currentStage} />
93
+ </div>
94
+ <div>Binding: {currentProject.binding}</div>
95
+ <div>Responsavel: {currentProject.owner}</div>
96
+ </CardContent>
97
+ </Card>
98
+ <Card>
99
+ <CardHeader>
100
+ <CardTitle className="text-base">Metricas</CardTitle>
101
+ </CardHeader>
102
+ <CardContent className="space-y-2 text-sm">
103
+ <div>Cenas: {scenes.length}</div>
104
+ <div>Sessoes: {sessions.length}</div>
105
+ <div>Takes: {takes.length}</div>
106
+ <div>
107
+ Assets:{' '}
108
+ {
109
+ STUDIO_ASSETS.filter((item) =>
110
+ takes.some((take) => take.id === item.takeId)
111
+ ).length
112
+ }
113
+ </div>
114
+ </CardContent>
115
+ </Card>
116
+ </TabsContent>
117
+
118
+ <TabsContent value="scenes">
119
+ <div className="rounded-md border">
120
+ <Table>
121
+ <TableHeader>
122
+ <TableRow>
123
+ <TableHead>Cena</TableHead>
124
+ <TableHead>Status</TableHead>
125
+ <TableHead>Duracao Est.</TableHead>
126
+ <TableHead>Takes</TableHead>
127
+ </TableRow>
128
+ </TableHeader>
129
+ <TableBody>
130
+ {scenes.map((scene) => (
131
+ <TableRow key={scene.id}>
132
+ <TableCell>{scene.name}</TableCell>
133
+ <TableCell>
134
+ <StudioStatusBadge value={scene.status} />
135
+ </TableCell>
136
+ <TableCell>{scene.estimatedSeconds}s</TableCell>
137
+ <TableCell>{scene.takes}</TableCell>
138
+ </TableRow>
139
+ ))}
140
+ </TableBody>
141
+ </Table>
142
+ </div>
143
+ </TabsContent>
144
+
145
+ <TabsContent value="sessions">
146
+ <div className="rounded-md border">
147
+ <Table>
148
+ <TableHeader>
149
+ <TableRow>
150
+ <TableHead>Titulo</TableHead>
151
+ <TableHead>Status</TableHead>
152
+ <TableHead>Tipo</TableHead>
153
+ <TableHead>Host</TableHead>
154
+ <TableHead>Participantes</TableHead>
155
+ <TableHead>Room</TableHead>
156
+ </TableRow>
157
+ </TableHeader>
158
+ <TableBody>
159
+ {sessions.map((session) => (
160
+ <TableRow key={session.id}>
161
+ <TableCell>{session.title}</TableCell>
162
+ <TableCell>
163
+ <StudioStatusBadge value={session.status} />
164
+ </TableCell>
165
+ <TableCell>{session.sessionType}</TableCell>
166
+ <TableCell>{session.host}</TableCell>
167
+ <TableCell>{session.participants}</TableCell>
168
+ <TableCell>{session.roomKey}</TableCell>
169
+ </TableRow>
170
+ ))}
171
+ </TableBody>
172
+ </Table>
173
+ </div>
174
+ </TabsContent>
175
+
176
+ <TabsContent value="takes">
177
+ <div className="rounded-md border">
178
+ <Table>
179
+ <TableHeader>
180
+ <TableRow>
181
+ <TableHead>Take</TableHead>
182
+ <TableHead>Status</TableHead>
183
+ <TableHead>Participantes</TableHead>
184
+ <TableHead>Upload</TableHead>
185
+ <TableHead>Integridade</TableHead>
186
+ </TableRow>
187
+ </TableHeader>
188
+ <TableBody>
189
+ {takes.map((take) => (
190
+ <TableRow key={take.id}>
191
+ <TableCell>#{take.id}</TableCell>
192
+ <TableCell>
193
+ <StudioStatusBadge value={take.status} />
194
+ </TableCell>
195
+ <TableCell>{take.participants}</TableCell>
196
+ <TableCell>
197
+ {take.uploadedFiles}/{take.expectedFiles}
198
+ </TableCell>
199
+ <TableCell>
200
+ <StudioStatusBadge value={take.integrity} />
201
+ </TableCell>
202
+ </TableRow>
203
+ ))}
204
+ </TableBody>
205
+ </Table>
206
+ </div>
207
+ </TabsContent>
208
+
209
+ <TabsContent value="assets">
210
+ <div className="rounded-md border">
211
+ <Table>
212
+ <TableHeader>
213
+ <TableRow>
214
+ <TableHead>Participante</TableHead>
215
+ <TableHead>Take</TableHead>
216
+ <TableHead>Tipo</TableHead>
217
+ <TableHead>Disponibilidade</TableHead>
218
+ </TableRow>
219
+ </TableHeader>
220
+ <TableBody>
221
+ {STUDIO_ASSETS.filter((item) =>
222
+ takes.some((take) => take.id === item.takeId)
223
+ ).map((asset) => (
224
+ <TableRow key={asset.id}>
225
+ <TableCell>{asset.participant}</TableCell>
226
+ <TableCell>#{asset.takeId}</TableCell>
227
+ <TableCell>{asset.sourceType}</TableCell>
228
+ <TableCell>
229
+ <StudioStatusBadge value={asset.availability} />
230
+ </TableCell>
231
+ </TableRow>
232
+ ))}
233
+ </TableBody>
234
+ </Table>
235
+ </div>
236
+ </TabsContent>
237
+
238
+ <TabsContent value="editing" className="grid gap-4 md:grid-cols-2">
239
+ <Card>
240
+ <CardHeader>
241
+ <CardTitle className="text-base">Pipeline</CardTitle>
242
+ </CardHeader>
243
+ <CardContent className="space-y-2 text-sm">
244
+ <div>Preparation: completed</div>
245
+ <div>Recording: in_progress</div>
246
+ <div>Rough cut: pending</div>
247
+ <div>Finalization: pending</div>
248
+ </CardContent>
249
+ </Card>
250
+ <Card>
251
+ <CardHeader>
252
+ <CardTitle className="text-base">Pacotes para editor</CardTitle>
253
+ </CardHeader>
254
+ <CardContent className="space-y-2 text-sm">
255
+ <div>Raw take bundle: available</div>
256
+ <div>Proxy bundle: generating</div>
257
+ </CardContent>
258
+ </Card>
259
+ </TabsContent>
260
+
261
+ <TabsContent value="publication">
262
+ <div className="rounded-md border">
263
+ <Table>
264
+ <TableHeader>
265
+ <TableRow>
266
+ <TableHead>Destino</TableHead>
267
+ <TableHead>Status</TableHead>
268
+ <TableHead>Agendamento</TableHead>
269
+ <TableHead>URL</TableHead>
270
+ </TableRow>
271
+ </TableHeader>
272
+ <TableBody>
273
+ {publicationTargets.map((target) => (
274
+ <TableRow key={target.id}>
275
+ <TableCell>{target.targetType}</TableCell>
276
+ <TableCell>
277
+ <StudioStatusBadge value={target.status} />
278
+ </TableCell>
279
+ <TableCell>
280
+ {target.scheduledAt
281
+ ? new Date(target.scheduledAt).toLocaleString('pt-BR')
282
+ : '-'}
283
+ </TableCell>
284
+ <TableCell>{target.publishedUrl || '-'}</TableCell>
285
+ </TableRow>
286
+ ))}
287
+ </TableBody>
288
+ </Table>
289
+ </div>
290
+ </TabsContent>
291
+
292
+ <TabsContent value="incidents">
293
+ <div className="rounded-md border">
294
+ <Table>
295
+ <TableHeader>
296
+ <TableRow>
297
+ <TableHead>Tipo</TableHead>
298
+ <TableHead>Severidade</TableHead>
299
+ <TableHead>Alvo</TableHead>
300
+ <TableHead>Status</TableHead>
301
+ </TableRow>
302
+ </TableHeader>
303
+ <TableBody>
304
+ {incidents.map((incident) => (
305
+ <TableRow key={incident.id}>
306
+ <TableCell>{incident.type}</TableCell>
307
+ <TableCell>
308
+ <StudioStatusBadge value={incident.severity} />
309
+ </TableCell>
310
+ <TableCell>{incident.target}</TableCell>
311
+ <TableCell>
312
+ <StudioStatusBadge value={incident.status} />
313
+ </TableCell>
314
+ </TableRow>
315
+ ))}
316
+ </TableBody>
317
+ </Table>
318
+ </div>
319
+ </TabsContent>
320
+ </Tabs>
321
+ </Page>
322
+ );
323
+ }