@jjlmoya/utils-performing-arts 1.1.0

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 (117) hide show
  1. package/.github/workflows/npm-publish.yml +40 -0
  2. package/.gitignore +6 -0
  3. package/.stylelintrc.json +98 -0
  4. package/astro.config.mjs +19 -0
  5. package/eslint.config.js +201 -0
  6. package/package.json +79 -0
  7. package/prompts/create_tool.md +98 -0
  8. package/prompts/i18n/de.md +16 -0
  9. package/prompts/i18n/en.md +16 -0
  10. package/prompts/i18n/es.md +16 -0
  11. package/prompts/i18n/fr.md +16 -0
  12. package/prompts/i18n/id.md +16 -0
  13. package/prompts/i18n/it.md +16 -0
  14. package/prompts/i18n/ja.md +16 -0
  15. package/prompts/i18n/ko.md +16 -0
  16. package/prompts/i18n/nl.md +16 -0
  17. package/prompts/i18n/pl.md +16 -0
  18. package/prompts/i18n/pt.md +16 -0
  19. package/prompts/i18n/ru.md +16 -0
  20. package/prompts/i18n/sv.md +16 -0
  21. package/prompts/i18n/tr.md +16 -0
  22. package/prompts/i18n/zh.md +16 -0
  23. package/prompts/seo.md +58 -0
  24. package/prompts/translations/french.md +33 -0
  25. package/scripts/postinstall.mjs +27 -0
  26. package/src/category/PerformingArtsCategorySEO.astro +9 -0
  27. package/src/category/i18n/de.ts +27 -0
  28. package/src/category/i18n/en.ts +27 -0
  29. package/src/category/i18n/es.ts +27 -0
  30. package/src/category/i18n/fr.ts +27 -0
  31. package/src/category/i18n/id.ts +27 -0
  32. package/src/category/i18n/it.ts +27 -0
  33. package/src/category/i18n/ja.ts +27 -0
  34. package/src/category/i18n/ko.ts +27 -0
  35. package/src/category/i18n/nl.ts +27 -0
  36. package/src/category/i18n/pl.ts +27 -0
  37. package/src/category/i18n/pt.ts +27 -0
  38. package/src/category/i18n/ru.ts +27 -0
  39. package/src/category/i18n/sv.ts +27 -0
  40. package/src/category/i18n/tr.ts +27 -0
  41. package/src/category/i18n/zh.ts +27 -0
  42. package/src/category/index.ts +10 -0
  43. package/src/components/PreviewNavSidebar.astro +116 -0
  44. package/src/components/PreviewToolbar.astro +143 -0
  45. package/src/data.ts +10 -0
  46. package/src/entries.ts +3 -0
  47. package/src/env.d.ts +5 -0
  48. package/src/index.ts +20 -0
  49. package/src/layouts/PreviewLayout.astro +118 -0
  50. package/src/pages/[locale]/[slug].astro +165 -0
  51. package/src/pages/[locale].astro +251 -0
  52. package/src/pages/index.astro +4 -0
  53. package/src/tests/bibliography_wellformed_export.test.ts +46 -0
  54. package/src/tests/category_seo_quality.test.ts +74 -0
  55. package/src/tests/diacritics_density.test.ts +118 -0
  56. package/src/tests/faq_count.test.ts +18 -0
  57. package/src/tests/i18n_coverage.test.ts +34 -0
  58. package/src/tests/inverted_punctuation.test.ts +84 -0
  59. package/src/tests/locale_completeness.test.ts +23 -0
  60. package/src/tests/mocks/astro_mock.js +2 -0
  61. package/src/tests/no_em_dash.test.ts +47 -0
  62. package/src/tests/no_en_dash.test.ts +70 -0
  63. package/src/tests/no_h1_in_components.test.ts +48 -0
  64. package/src/tests/pagespeed_best_practices.test.ts +198 -0
  65. package/src/tests/qa-test-helpers.ts +32 -0
  66. package/src/tests/qa_bibliography_links.test.ts +43 -0
  67. package/src/tests/qa_claim_evidence.test.ts +69 -0
  68. package/src/tests/qa_logic_reference_coverage.test.ts +46 -0
  69. package/src/tests/qa_runtime_i18n.test.ts +100 -0
  70. package/src/tests/schemas_fulfillment.test.ts +23 -0
  71. package/src/tests/script_density.test.ts +94 -0
  72. package/src/tests/seo_length.test.ts +22 -0
  73. package/src/tests/seo_parity.test.ts +60 -0
  74. package/src/tests/seo_translation_completeness.test.ts +69 -0
  75. package/src/tests/seo_wellformed_export.test.ts +65 -0
  76. package/src/tests/shared-test-helpers.ts +56 -0
  77. package/src/tests/slug_language_code_format.test.ts +23 -0
  78. package/src/tests/slug_uniqueness.test.ts +81 -0
  79. package/src/tests/spanish_leakage.test.ts +175 -0
  80. package/src/tests/title_quality.test.ts +55 -0
  81. package/src/tests/tool_exports.test.ts +34 -0
  82. package/src/tests/tool_validation.test.ts +16 -0
  83. package/src/tests/translation_copy.test.ts +115 -0
  84. package/src/tool/rehearsal-call-sheet-planner/bibliography.astro +6 -0
  85. package/src/tool/rehearsal-call-sheet-planner/bibliography.ts +12 -0
  86. package/src/tool/rehearsal-call-sheet-planner/component.astro +74 -0
  87. package/src/tool/rehearsal-call-sheet-planner/controller.ts +98 -0
  88. package/src/tool/rehearsal-call-sheet-planner/dom-views.ts +86 -0
  89. package/src/tool/rehearsal-call-sheet-planner/entry.ts +27 -0
  90. package/src/tool/rehearsal-call-sheet-planner/evaluator.ts +27 -0
  91. package/src/tool/rehearsal-call-sheet-planner/i18n/de.ts +60 -0
  92. package/src/tool/rehearsal-call-sheet-planner/i18n/en.ts +158 -0
  93. package/src/tool/rehearsal-call-sheet-planner/i18n/es.ts +60 -0
  94. package/src/tool/rehearsal-call-sheet-planner/i18n/fr.ts +60 -0
  95. package/src/tool/rehearsal-call-sheet-planner/i18n/id.ts +47 -0
  96. package/src/tool/rehearsal-call-sheet-planner/i18n/it.ts +47 -0
  97. package/src/tool/rehearsal-call-sheet-planner/i18n/ja.ts +47 -0
  98. package/src/tool/rehearsal-call-sheet-planner/i18n/ko.ts +47 -0
  99. package/src/tool/rehearsal-call-sheet-planner/i18n/nl.ts +47 -0
  100. package/src/tool/rehearsal-call-sheet-planner/i18n/pl.ts +47 -0
  101. package/src/tool/rehearsal-call-sheet-planner/i18n/pt.ts +47 -0
  102. package/src/tool/rehearsal-call-sheet-planner/i18n/ru.ts +48 -0
  103. package/src/tool/rehearsal-call-sheet-planner/i18n/sv.ts +48 -0
  104. package/src/tool/rehearsal-call-sheet-planner/i18n/tr.ts +48 -0
  105. package/src/tool/rehearsal-call-sheet-planner/i18n/zh.ts +48 -0
  106. package/src/tool/rehearsal-call-sheet-planner/index.ts +11 -0
  107. package/src/tool/rehearsal-call-sheet-planner/logic.test.ts +68 -0
  108. package/src/tool/rehearsal-call-sheet-planner/logic.ts +229 -0
  109. package/src/tool/rehearsal-call-sheet-planner/rehearsal-call-sheet-planner.css +593 -0
  110. package/src/tool/rehearsal-call-sheet-planner/seo.astro +16 -0
  111. package/src/tool/rehearsal-call-sheet-planner/storage.ts +33 -0
  112. package/src/tool/rehearsal-call-sheet-planner/types.ts +84 -0
  113. package/src/tool/rehearsal-call-sheet-planner/ui.ts +70 -0
  114. package/src/tools.ts +8 -0
  115. package/src/types.ts +68 -0
  116. package/tsconfig.json +15 -0
  117. package/vitest.config.ts +20 -0
@@ -0,0 +1,86 @@
1
+ import { evaluatePlan } from './evaluator';
2
+ import { formatTime } from './logic';
3
+ import type { CallSheetResult, InputIssueSection, SceneIssue, SceneStatus } from './types';
4
+
5
+ type Labels = Record<string, string>;
6
+
7
+ function escapeHtml(value: unknown): string {
8
+ return String(value ?? '').replace(/[&<>"']/g, (character) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[character] ?? character));
9
+ }
10
+
11
+ function statusLabel(status: SceneStatus, labels: Labels): string {
12
+ let key = 'statusUnscheduled';
13
+ if (status === 'scheduled') key = 'statusScheduled';
14
+ if (status === 'conflict') key = 'statusConflict';
15
+ return labels[key] ?? status;
16
+ }
17
+
18
+ function issueLabel(issue: SceneIssue, labels: Labels): string {
19
+ const labelsByKey: Record<string, string | undefined> = {
20
+ dayOverflow: labels.issueDayOverflow,
21
+ participantAvailability: labels.issueParticipantAvailability,
22
+ roomAvailability: labels.issueRoomAvailability,
23
+ missingParticipantAvailability: labels.issueMissingParticipantAvailability,
24
+ missingRoomAvailability: labels.issueMissingRoomAvailability,
25
+ };
26
+ const label = labelsByKey[issue.key] ?? issue.key;
27
+ return `${escapeHtml(label)} ${issue.key === 'dayOverflow' ? '' : `<strong>${escapeHtml(issue.detail)}</strong>`}`;
28
+ }
29
+
30
+ function inputSectionLabel(section: InputIssueSection, labels: Labels): string {
31
+ if (section === 'people') return labels.peopleInput ?? labels.scenesInput ?? '';
32
+ if (section === 'rooms') return labels.roomsInput ?? labels.scenesInput ?? '';
33
+ return labels.scenesInput ?? '';
34
+ }
35
+
36
+ function summaryMessage(result: CallSheetResult, labels: Labels, conflictCount: number): string {
37
+ if (result.inputIssues.length > 0) return labels.summaryInputIssues ?? '';
38
+ if (conflictCount > 0) return labels.summaryAttention ?? '';
39
+ return labels.summaryReady ?? '';
40
+ }
41
+
42
+ function renderInputIssues(result: CallSheetResult, labels: Labels): string {
43
+ if (result.inputIssues.length === 0) return '';
44
+ const items = result.inputIssues.map((issue) => `<li><strong>${escapeHtml(inputSectionLabel(issue.section, labels))} · ${escapeHtml(labels.line)} ${escapeHtml(issue.line)}</strong><code>${escapeHtml(issue.value)}</code><span>${escapeHtml(issue.detail)}</span></li>`).join('');
45
+ return `<section class="input-issues" aria-label="${escapeHtml(labels.inputIssuesTitle)}" role="status"><div class="input-issues-heading"><h3>${escapeHtml(labels.inputIssuesTitle)}</h3><span>${escapeHtml(labels.inputIssuesHint)}</span></div><ul>${items}</ul></section>`;
46
+ }
47
+
48
+ function renderSummary(result: CallSheetResult, labels: Labels): string {
49
+ const evaluation = evaluatePlan(result);
50
+ const message = summaryMessage(result, labels, evaluation.conflictScenes + evaluation.unscheduledScenes);
51
+ return `<div class="plan-summary" aria-label="${escapeHtml(labels.resultSection)}"><div><strong>${evaluation.scheduledScenes}</strong><span>${escapeHtml(labels.scheduled)}</span></div><div><strong>${evaluation.conflictScenes}</strong><span>${escapeHtml(labels.conflicts)}</span></div><div><strong>${evaluation.unscheduledScenes}</strong><span>${escapeHtml(labels.unscheduled)}</span></div><div><strong>${result.scheduledMinutes}</strong><span>${escapeHtml(labels.minutes)}</span></div></div><p class="plan-message">${escapeHtml(message)}</p>`;
52
+ }
53
+
54
+ function renderTimeline(result: CallSheetResult, labels: Labels): string {
55
+ if (result.scenes.length === 0) return `<div class="empty-state">${escapeHtml(labels.noScenes)}</div>`;
56
+ const span = Math.max(result.dayEnd - result.dayStart, 1);
57
+ const cards = result.scenes.map((scene) => {
58
+ const width = Math.max((scene.duration / span) * 100, 5);
59
+ const issueText = scene.issues.map((issue) => `<li>${issueLabel(issue, labels)}</li>`).join('');
60
+ return `<article class="timeline-card ${scene.status}" style="--scene-width:${width.toFixed(2)}%"><div class="timeline-card-top"><span>${escapeHtml(formatTime(scene.start))} to ${escapeHtml(formatTime(scene.end))}</span><b>${escapeHtml(statusLabel(scene.status, labels))}</b></div><h3>${escapeHtml(scene.name)}</h3><p>${escapeHtml(scene.room || labels.noRoom)} · ${escapeHtml(scene.duration)} ${escapeHtml(labels.minutes)}</p>${issueText ? `<ul>${issueText}</ul>` : ''}</article>`;
61
+ }).join('');
62
+ return `<div class="timeline" aria-label="${escapeHtml(labels.timelineLabel)}"><div class="timeline-scale"><span>${escapeHtml(formatTime(result.dayStart))}</span><span>${escapeHtml(formatTime(result.dayEnd))}</span></div><div class="timeline-track">${cards}</div></div>`;
63
+ }
64
+
65
+ function renderSceneRows(result: CallSheetResult, labels: Labels): string {
66
+ return result.scenes.map((scene) => {
67
+ const issues = scene.issues.map((issue) => issueLabel(issue, labels)).join('<br>');
68
+ const details = issues || escapeHtml(scene.notes || labels.readyDetail);
69
+ return `<tr><td><strong>${escapeHtml(formatTime(scene.start))} to ${escapeHtml(formatTime(scene.end))}</strong><small>${escapeHtml(scene.duration)} ${escapeHtml(labels.minutes)}</small></td><td>${escapeHtml(scene.name)}<small>${escapeHtml(scene.notes)}</small></td><td>${escapeHtml(scene.room || labels.noRoom)}</td><td>${escapeHtml(scene.participants.join(', ') || labels.noPeople)}</td><td><span class="status-pill ${scene.status}">${escapeHtml(statusLabel(scene.status, labels))}</span><small>${details}</small></td></tr>`;
70
+ }).join('');
71
+ }
72
+
73
+ function renderCallSheet(result: CallSheetResult, labels: Labels): string {
74
+ if (result.scenes.length === 0) return '';
75
+ return `<div class="call-sheet" aria-label="${escapeHtml(labels.callSheetLabel)}"><div class="call-sheet-heading"><div><span class="eyebrow">${escapeHtml(result.settings.date || labels.dateNotSet)}</span><h3>${escapeHtml(result.settings.showName || labels.untitledProduction)}</h3></div><p>${escapeHtml(result.settings.dayStart)} to ${escapeHtml(result.settings.dayEnd)} · ${escapeHtml(result.settings.breakMinutes)} ${escapeHtml(labels.minutes)} between scenes</p></div><div class="table-wrap"><table><thead><tr><th>${escapeHtml(labels.time)}</th><th>${escapeHtml(labels.scene)}</th><th>${escapeHtml(labels.room)}</th><th>${escapeHtml(labels.participants)}</th><th>${escapeHtml(labels.status)}<br>${escapeHtml(labels.detail)}</th></tr></thead><tbody>${renderSceneRows(result, labels)}</tbody></table></div></div>`;
76
+ }
77
+
78
+ function renderSummaryList(result: CallSheetResult, labels: Labels): string {
79
+ const people = result.people.map((person) => `<li><strong>${escapeHtml(person.name)}</strong><span>${escapeHtml(labels.firstCall)} ${escapeHtml(formatTime(person.firstCall))}</span><small>${escapeHtml(labels.lastCall)} ${escapeHtml(formatTime(person.lastCall))} · ${escapeHtml(person.sceneCount)} ${escapeHtml(person.sceneCount === 1 ? labels.scene : labels.scenes)}</small></li>`).join('');
80
+ const rooms = result.rooms.map((room) => `<li><strong>${escapeHtml(room.name)}</strong><span>${escapeHtml(labels.firstCall)} ${escapeHtml(formatTime(room.firstCall))}</span><small>${escapeHtml(labels.lastCall)} ${escapeHtml(formatTime(room.lastCall))} · ${escapeHtml(room.sceneCount)} ${escapeHtml(room.sceneCount === 1 ? labels.scene : labels.scenes)}</small></li>`).join('');
81
+ return `<div class="summary-lists"><section><h3>${escapeHtml(labels.participantSummary)}</h3><ul>${people || `<li>${escapeHtml(labels.noScenes)}</li>`}</ul></section><section><h3>${escapeHtml(labels.roomSummary)}</h3><ul>${rooms || `<li>${escapeHtml(labels.noScenes)}</li>`}</ul></section></div>`;
82
+ }
83
+
84
+ export function renderResult(result: CallSheetResult, labels: Labels): string {
85
+ return `${renderSummary(result, labels)}${renderInputIssues(result, labels)}${renderTimeline(result, labels)}${renderCallSheet(result, labels)}${renderSummaryList(result, labels)}`;
86
+ }
@@ -0,0 +1,27 @@
1
+ import type { PerformingArtsToolEntry, ToolLocaleContent } from '../../types';
2
+ import type { RehearsalCallSheetUI } from './ui';
3
+
4
+ export type { RehearsalCallSheetUI } from './ui';
5
+ export type RehearsalCallSheetLocaleContent = ToolLocaleContent<RehearsalCallSheetUI>;
6
+
7
+ export const rehearsalCallSheetPlanner: PerformingArtsToolEntry<RehearsalCallSheetUI> = {
8
+ id: 'rehearsal-call-sheet-planner',
9
+ icons: { bg: 'mdi:theater', fg: 'mdi:calendar-clock' },
10
+ i18n: {
11
+ de: () => import('./i18n/de').then((module) => module.content),
12
+ en: () => import('./i18n/en').then((module) => module.content),
13
+ es: () => import('./i18n/es').then((module) => module.content),
14
+ fr: () => import('./i18n/fr').then((module) => module.content),
15
+ id: () => import('./i18n/id').then((module) => module.content),
16
+ it: () => import('./i18n/it').then((module) => module.content),
17
+ ja: () => import('./i18n/ja').then((module) => module.content),
18
+ ko: () => import('./i18n/ko').then((module) => module.content),
19
+ nl: () => import('./i18n/nl').then((module) => module.content),
20
+ pl: () => import('./i18n/pl').then((module) => module.content),
21
+ pt: () => import('./i18n/pt').then((module) => module.content),
22
+ ru: () => import('./i18n/ru').then((module) => module.content),
23
+ sv: () => import('./i18n/sv').then((module) => module.content),
24
+ tr: () => import('./i18n/tr').then((module) => module.content),
25
+ zh: () => import('./i18n/zh').then((module) => module.content),
26
+ },
27
+ };
@@ -0,0 +1,27 @@
1
+ import type { CallSheetResult, SceneStatus } from './types';
2
+
3
+ export interface PlanEvaluation {
4
+ totalScenes: number;
5
+ scheduledScenes: number;
6
+ conflictScenes: number;
7
+ unscheduledScenes: number;
8
+ completionRatio: number;
9
+ dominantStatus: SceneStatus | 'empty';
10
+ }
11
+
12
+ export function evaluatePlan(result: CallSheetResult): PlanEvaluation {
13
+ const totalScenes = result.scenes.length;
14
+ const scheduledScenes = result.scenes.filter((scene) => scene.status === 'scheduled').length;
15
+ const conflictScenes = result.conflictCount;
16
+ const unscheduledScenes = result.unscheduledCount;
17
+ const completionRatio = totalScenes === 0 ? 0 : (scheduledScenes + conflictScenes) / totalScenes;
18
+ const dominantStatus = getDominantStatus({ scheduledScenes, conflictScenes, unscheduledScenes, totalScenes });
19
+ return { totalScenes, scheduledScenes, conflictScenes, unscheduledScenes, completionRatio, dominantStatus };
20
+ }
21
+
22
+ function getDominantStatus(counts: Pick<PlanEvaluation, 'scheduledScenes' | 'conflictScenes' | 'unscheduledScenes' | 'totalScenes'>): PlanEvaluation['dominantStatus'] {
23
+ if (counts.totalScenes === 0) return 'empty';
24
+ if (counts.unscheduledScenes >= counts.conflictScenes && counts.unscheduledScenes >= counts.scheduledScenes) return 'unscheduled';
25
+ if (counts.conflictScenes >= counts.scheduledScenes) return 'conflict';
26
+ return 'scheduled';
27
+ }
@@ -0,0 +1,60 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RehearsalCallSheetLocaleContent, RehearsalCallSheetUI } from '../entry';
4
+
5
+ const ui: RehearsalCallSheetUI = {
6
+ productionSection: 'Produktion', showName: 'Name der Produktion', showNamePlaceholder: 'Der zerbrochne Krug', date: 'Probentag',
7
+ timingSection: 'Arbeitstag', dayStart: 'Arbeitsbeginn', dayEnd: 'Arbeitsende', breakMinutes: 'Pause zwischen Szenen',
8
+ sceneSection: 'Szenen und Einheiten', scenesLabel: 'Szenenliste', scenesHint: 'Eine Zeile pro Szene: Name | Minuten | Raum | Personen mit Komma | Notiz. Beispiel: Fechtprobe | 45 | Studio | Alex, Bea | Langsam arbeiten.',
9
+ availabilitySection: 'Verfügbarkeiten', participantAvailability: 'Personen', participantHint: 'Eine Zeile pro Person: Name | erster Ruf bis letzter Abgang. Namen müssen mit der Szenenliste übereinstimmen.',
10
+ roomAvailability: 'Räume', roomHint: 'Eine Zeile pro Raum: Raum | Öffnungszeit bis Schließzeit. Beispiel: Studio | 09:00-18:00.',
11
+ notes: 'Produktionsnotizen', notesPlaceholder: 'Requisiten, Kleidung, Zugang oder Sicherheitshinweise für den Tag',
12
+ presets: 'Mit einem Entwurf starten', presetDay: 'Ganzer Probentag', presetTight: 'Kurzer Raumplan', presetShowcase: 'Vorstellungsdurchlauf',
13
+ resultSection: 'Probenplan', scheduled: 'Geplant', conflicts: 'Konflikte', unscheduled: 'Nicht eingeplant', minutes: 'Minuten', timelineLabel: 'Probenzeitplan', callSheetLabel: 'Druckbarer Probenplan',
14
+ statusScheduled: 'Geplant', statusConflict: 'Ruf prüfen', statusUnscheduled: 'Nicht eingeplant', issueDayOverflow: 'Diese Einheit endet nach dem Arbeitstag', issueParticipantAvailability: 'außerhalb der Verfügbarkeit von', issueRoomAvailability: 'außerhalb der Raumverfügbarkeit von', issueMissingParticipantAvailability: 'keine Verfügbarkeit eingetragen für', issueMissingRoomAvailability: 'keine Verfügbarkeit eingetragen für den Raum',
15
+ participantSummary: 'Rufe nach Person', roomSummary: 'Raumbelegung', scene: 'Szene', scenes: 'Szenen', firstCall: 'Erster Ruf', lastCall: 'Letzter Abgang', room: 'Raum', participants: 'Personen', time: 'Zeit', status: 'Status', detail: 'Details',
16
+ dateNotSet: 'Datum nicht festgelegt', untitledProduction: 'Produktion ohne Titel', noRoom: 'Kein Raum eingetragen', noPeople: 'Keine Personen eingetragen', readyDetail: 'Bereit für die Probe', inputIssuesTitle: 'Diese Zeilen prüfen', inputIssuesHint: 'Diese Zeilen wurden bis zur Korrektur übersprungen.', scenesInput: 'Szenen', peopleInput: 'Personenverfügbarkeit', roomsInput: 'Raumverfügbarkeit', line: 'Zeile',
17
+ summaryReady: 'Dieser Entwurf kann vom Produktionsteam geprüft und gedruckt werden.', summaryAttention: 'Prüfe die markierten Rufe, bevor du den Entwurf verschickst.', summaryInputIssues: 'Korrigiere die übersprungenen Zeilen, bevor du dich auf den Plan verlässt.', noScenes: 'Füge eine Szenenzeile hinzu, um den Probenzeitplan zu zeichnen.', exportData: 'JSON herunterladen', print: 'Probenplan drucken', reset: 'Entwurf zurücksetzen',
18
+ rulesNote: 'Dies ist ein Planungsentwurf und kein rechtsverbindlicher Rufplan. Prüfe Produktionsvereinbarungen, Hausregeln und die örtlichen Arbeitszeitvorschriften vor dem Versand.', rulesLinkLabel: 'Britische Hinweise als Referenz lesen',
19
+ };
20
+
21
+ const faq = [
22
+ { question: 'Was ist ein Probenrufplan?', answer: 'Er ist der praktische Tagesplan für eine Probe: wann welche Szene stattfindet, wer dabei sein muss, welcher Raum genutzt wird und was das Team mitbringen oder beachten soll. Dieser Planer macht daraus einen prüfbaren und druckbaren Entwurf.' },
23
+ { question: 'Wie formatiere ich die Szenenliste?', answer: 'Schreibe eine Szene pro Zeile und trenne Name, Dauer in Minuten, Raum, durch Komma getrennte Personen und optionale Notizen mit senkrechten Strichen. Beispiel: Fechtprobe | 45 | Studio | Alex, Bea | Langsam arbeiten.' },
24
+ { question: 'Was bedeutet ein Konflikt?', answer: 'Ein Konflikt bedeutet, dass eine Person oder ein Raum während der gesamten Einheit nicht verfügbar ist oder dass ein Verfügbarkeitseintrag fehlt. Die Einheit bleibt sichtbar, damit du sie anpassen kannst.' },
25
+ { question: 'Findet der Planer den perfekten Zeitplan?', answer: 'Nein. Er verwendet die Reihenfolge deiner Szenen und prüft sie gegen die eingetragenen Zeitfenster. Reisezeit, Verträge, Besetzung, parallele Räume und Rechtskonformität werden nicht optimiert.' },
26
+ { question: 'Wo werden Namen und Notizen gespeichert?', answer: 'Der aktuelle Entwurf wird in diesem Browser auf diesem Gerät gespeichert. Der Planer selbst lädt diese Angaben nicht hoch. Nutze Entwurf zurücksetzen oder lösche die Websitedaten des Browsers.' },
27
+ { question: 'Macht das Pausenfeld den Rufplan rechtskonform?', answer: 'Nein. Es fügt zwischen Einheiten eine Planungspause ein. Arbeitszeit, Erholung, Schutz, Tarifregeln und Beschäftigungsrecht hängen von Ort und Produktion ab.' },
28
+ ];
29
+
30
+ const howTo = [
31
+ { name: 'Produktion benennen', text: 'Trage den Namen des Stücks oder Projekts und das Probendatum ein, damit der Ausdruck eindeutig ist.' },
32
+ { name: 'Arbeitstag festlegen', text: 'Wähle Beginn, Ende und die geplante Pause zwischen Einheiten. Bei 15 Minuten Pause folgt auf eine 60-minütige Szene um 09:00 der nächste Ruf um 10:15.' },
33
+ { name: 'Szenen eintragen', text: 'Füge pro Zeile Dauer, Raum, Personen und nützliche Notizen mit senkrechten Strichen ein.' },
34
+ { name: 'Verfügbarkeit ergänzen', text: 'Trage für jede Person und jeden Raum ein Zeitfenster ein und verwende dieselben Namen wie in der Szenenliste.' },
35
+ { name: 'Prüfen und teilen', text: 'Lies Zeitplan und Konflikte, korrigiere offene Rufe und drucke den Probenplan oder lade den JSON-Entwurf herunter.' },
36
+ ];
37
+
38
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
39
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Probenrufplan', operatingSystem: 'All', applicationCategory: 'BusinessApplication', description: 'Erstellt einen prüfbaren Probenrufplan aus Szenen, Personen, Räumen, Verfügbarkeiten und Pausen.' };
40
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Einen Probenrufplan erstellen', step: howTo.map((item) => ({ '@type': 'HowToStep', name: item.name, text: item.text })) };
41
+
42
+ export const content: RehearsalCallSheetLocaleContent = {
43
+ slug: 'probenrufplan-theater', title: 'Probenrufplan', description: 'Erstelle einen Probenrufplan für das Theater aus Szenen, Besetzung, Räumen, Verfügbarkeit und Pausen. Prüfe Konflikte vor dem Ruf.', ui,
44
+ seo: [
45
+ { type: 'title', text: 'Einen Probenrufplan erstellen', level: 2 },
46
+ { type: 'paragraph', html: 'Ein Probenrufplan ist der Tagesplan für Regie, Inspizienz, Darsteller und Team. Dieser Planer macht aus deiner Szenenreihenfolge einen zeitlichen Entwurf, prüft Personen und Räume und zeigt, was vor dem Versand geklärt werden muss.' },
47
+ { type: 'title', text: 'Was in einen guten Probenrufplan gehört', level: 2 },
48
+ { type: 'paragraph', html: 'Eine brauchbare Liste enthält nicht nur Szenennamen. Jede Einheit braucht eine Dauer, einen Raum, die beteiligten Personen und eine Notiz, wenn Requisiten, Kleidung, Vorbereitung oder besondere Arbeitsbedingungen wichtig sind.' },
49
+ { type: 'table', headers: ['Eingabe', 'Was der Planer tut'], rows: [['Szenen', 'Ordnet Einheiten in deiner Reihenfolge an'], ['Personen', 'Prüft jeden Ruf gegen ein Zeitfenster'], ['Räume', 'Prüft den Raum für die gesamte Einheit'], ['Pausen', 'Fügt eine geplante Lücke zwischen Szenen ein'], ['Notizen', 'Hält praktischen Kontext im Ausdruck fest']] },
50
+ { type: 'title', text: 'Den Zeitplan lesen', level: 2 },
51
+ { type: 'paragraph', html: 'Der Zeitplan übernimmt deine Reihenfolge und erfindet keine künstlerische Priorität. Startet eine Szene um 09:00, dauert 60 Minuten und folgt eine Pause von 15 Minuten, beginnt die nächste um 10:15. Ein Konflikt passt nicht zum Zeitfenster einer Person oder eines Raums.' },
52
+ { type: 'title', text: 'Den Entwurf vor dem Versand prüfen', level: 2 },
53
+ { type: 'list', items: ['Bestätige die Reihenfolge mit Regie und Inspizienz.', 'Prüfe Wege, Zugang, Kostüm, Requisiten, Schutz und Zeit für den Raumwechsel.', 'Ergänze fehlende Verfügbarkeiten mit einem bestätigten Ruf.', 'Prüfe lokale Vereinbarungen und Arbeitszeitregeln mit dem verantwortlichen Team.'] },
54
+ { type: 'title', text: 'Ein transparenter Entwurf mit menschlichem Urteil', level: 2 },
55
+ { type: 'paragraph', html: 'Der Planer kennt weder die beste künstlerische Reihenfolge noch Wege, Zugang, Schutzanforderungen oder lokale Arbeitszeitregeln. Er liefert einen lesbaren Probenrufplan, den das echte Produktionsteam gemeinsam prüfen kann.' },
56
+ { type: 'tip', title: 'Verfügbarkeit als Gesprächsanstoß nutzen', html: 'Ein fehlendes oder widersprüchliches Zeitfenster ist kein Urteil über eine Person. Es zeigt, dass der Ruf bestätigt, die Reihenfolge geändert, der Raum gewechselt oder die Information ergänzt werden muss.' },
57
+ { type: 'title', text: 'Einen verwendbaren Probenrufplan drucken', level: 2 },
58
+ { type: 'paragraph', html: 'Löse markierte Rufe zuerst, drucke dann den Plan für den Probenraum oder lade den JSON-Entwurf für deinen Produktionsablauf herunter. Reihenfolge, Zeiten, Räume, Personen, Notizen und Prüfstatus bleiben zusammen.' },
59
+ ], faq, bibliography, howTo, schemas: [faqSchema, appSchema, howToSchema] as unknown as Record<string, unknown>[],
60
+ };
@@ -0,0 +1,158 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RehearsalCallSheetLocaleContent, RehearsalCallSheetUI } from '../entry';
4
+
5
+ const ui: RehearsalCallSheetUI = {
6
+ productionSection: 'Production',
7
+ showName: 'Production name',
8
+ showNamePlaceholder: 'The Glass Menagerie',
9
+ date: 'Rehearsal date',
10
+ timingSection: 'Working day',
11
+ dayStart: 'Day starts',
12
+ dayEnd: 'Day ends',
13
+ breakMinutes: 'Break between scenes',
14
+ sceneSection: 'Scenes and sessions',
15
+ scenesLabel: 'Scene list',
16
+ scenesHint: 'One per line: Name | minutes | room | people separated by commas | note. Example: Fight call | 45 | Studio | Alex, Bea | Slow work only.',
17
+ availabilitySection: 'Availability windows',
18
+ participantAvailability: 'People',
19
+ participantHint: 'One per line: Name | first call to last out. Names must match the scene list.',
20
+ roomAvailability: 'Rooms',
21
+ roomHint: 'One per line: Room | opening time to closing time. Example: Studio | 09:00-18:00.',
22
+ notes: 'Production notes',
23
+ notesPlaceholder: 'Props, clothing, access, or safety notes for the whole day',
24
+ presets: 'Start with a draft',
25
+ presetDay: 'Full rehearsal day',
26
+ presetTight: 'Tight room call',
27
+ presetShowcase: 'Showcase run',
28
+ resultSection: 'Call sheet',
29
+ scheduled: 'Scheduled',
30
+ conflicts: 'Conflicts',
31
+ unscheduled: 'Not scheduled',
32
+ minutes: 'minutes',
33
+ timelineLabel: 'Rehearsal timeline',
34
+ callSheetLabel: 'Printable rehearsal call sheet',
35
+ statusScheduled: 'Scheduled',
36
+ statusConflict: 'Review call',
37
+ statusUnscheduled: 'Not scheduled',
38
+ issueDayOverflow: 'This session passes the working day end',
39
+ issueParticipantAvailability: 'outside availability for',
40
+ issueRoomAvailability: 'outside room availability for',
41
+ issueMissingParticipantAvailability: 'no availability recorded for',
42
+ issueMissingRoomAvailability: 'no availability recorded for room',
43
+ participantSummary: 'Calls by person',
44
+ roomSummary: 'Room calls',
45
+ scene: 'Scene',
46
+ scenes: 'Scenes',
47
+ firstCall: 'First call',
48
+ lastCall: 'Last out',
49
+ room: 'Room',
50
+ participants: 'People',
51
+ time: 'Time',
52
+ status: 'Status',
53
+ detail: 'Detail',
54
+ dateNotSet: 'Date not set',
55
+ untitledProduction: 'Untitled production',
56
+ noRoom: 'No room listed',
57
+ noPeople: 'No people listed',
58
+ readyDetail: 'Ready to rehearse',
59
+ inputIssuesTitle: 'Check the lines below',
60
+ inputIssuesHint: 'These lines were skipped until corrected.',
61
+ scenesInput: 'Scenes',
62
+ peopleInput: 'People availability',
63
+ roomsInput: 'Room availability',
64
+ line: 'line',
65
+ summaryReady: 'This draft is ready for a production review and print.',
66
+ summaryAttention: 'Review the highlighted calls before you send this draft.',
67
+ summaryInputIssues: 'Correct the skipped lines before you rely on the schedule.',
68
+ noScenes: 'Add a scene line to draw the rehearsal timeline.',
69
+ exportData: 'Export JSON',
70
+ print: 'Print call sheet',
71
+ reset: 'Reset draft',
72
+ rulesNote: 'This is a planning draft, not a legal call sheet. Check your production agreements, venue rules, and local working time requirements before sending it.',
73
+ rulesLinkLabel: 'Read UK guidance as one reference',
74
+ };
75
+
76
+ const faq = [
77
+ {
78
+ question: 'What is a rehearsal call sheet?',
79
+ answer: 'It is the practical plan for a rehearsal day: when each scene is worked, who needs to attend, which room is used, and what the team needs to bring or remember. This planner turns that information into a draft you can check and print.',
80
+ },
81
+ {
82
+ question: 'How do I format the scene list?',
83
+ answer: 'Use one scene per line with a vertical bar between the scene name, duration in minutes, room, participants separated by commas, and optional notes. For example, Blocking scene one | 90 | Studio | Alex, Bea | Bring rehearsal props.',
84
+ },
85
+ {
86
+ question: 'What does a conflict mean?',
87
+ answer: 'A conflict means the proposed session is inside the working day but at least one person or room is not available for the complete session, or that an availability record is missing. The planner keeps the session visible so you can adjust it instead of silently deleting it.',
88
+ },
89
+ {
90
+ question: 'Does the planner find the perfect schedule?',
91
+ answer: 'No. It lays sessions in the order you provide and checks each one against the entered windows. It does not optimize parallel rooms, travel, contracts, casting, or legal compliance. Use the result to challenge the order with the production team.',
92
+ },
93
+ {
94
+ question: 'Where are names and notes stored?',
95
+ answer: 'The current draft is stored in this browser on this device. Nothing is uploaded by the planner itself. Use Reset draft or clear the browser site data if you need to remove the saved draft.',
96
+ },
97
+ {
98
+ question: 'Does the break field make the call legally compliant?',
99
+ answer: 'No. It inserts a planning gap between sessions. Working time, rest, safeguarding, union, venue, and employment rules vary by place and production, so the final call must be checked by the responsible team.',
100
+ },
101
+ ];
102
+
103
+ const howTo = [
104
+ { name: 'Name the production', text: 'Enter the show or project name and the rehearsal date so the printed call sheet has a clear heading.' },
105
+ { name: 'Set the working day', text: 'Choose the start, end, and planning gap between sessions. A 15 minute gap means a 60 minute scene starting at 09:00 is followed by a 10:15 call.' },
106
+ { name: 'Enter the scenes', text: 'Add one pipe separated line per scene with its duration, room, participants, and useful notes.' },
107
+ { name: 'Add availability', text: 'Give each participant and room an availability window using the same names as the scene list.' },
108
+ { name: 'Review and share', text: 'Read the timeline, resolve conflicts, then print the call sheet or export the local JSON draft.' },
109
+ ];
110
+
111
+ const faqSchema: WithContext<FAQPage> = {
112
+ '@context': 'https://schema.org',
113
+ '@type': 'FAQPage',
114
+ mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })),
115
+ };
116
+
117
+ const appSchema: WithContext<SoftwareApplication> = {
118
+ '@context': 'https://schema.org',
119
+ '@type': 'SoftwareApplication',
120
+ name: 'Rehearsal Call Sheet',
121
+ operatingSystem: 'All',
122
+ applicationCategory: 'BusinessApplication',
123
+ description: 'Create a theatre rehearsal call sheet from scenes, cast, rooms, availability and breaks. Print a clear daily plan and flag conflicts before the call.',
124
+ };
125
+
126
+ const howToSchema: WithContext<HowTo> = {
127
+ '@context': 'https://schema.org',
128
+ '@type': 'HowTo',
129
+ name: 'How to build a rehearsal call sheet',
130
+ step: howTo.map((item) => ({ '@type': 'HowToStep', name: item.name, text: item.text })),
131
+ };
132
+
133
+ export const content: RehearsalCallSheetLocaleContent = {
134
+ slug: 'rehearsal-call-sheet-planner',
135
+ title: 'Rehearsal Call Sheet',
136
+ description: 'Create a theatre rehearsal call sheet from scenes, cast, rooms, availability and breaks. Print a clear daily plan and flag conflicts before the call.',
137
+ ui,
138
+ seo: [
139
+ { type: 'title', text: 'How to Build a Rehearsal Call Sheet', level: 2 },
140
+ { type: 'paragraph', html: 'A rehearsal call sheet is the day plan shared by the stage manager, director, performers, and crew. This planner turns your scene order into a timed draft, checks attendance and room windows, and leaves you with a readable sheet to review before the call.' },
141
+ { type: 'title', text: 'What a Useful Call Sheet Includes', level: 2 },
142
+ { type: 'paragraph', html: 'The useful part is not just a list of scenes. Every entry needs a duration, room, people, and a note when the team must bring something, prepare a prop, or protect a specific working condition. The result makes the next production conversation concrete.' },
143
+ { type: 'table', headers: ['Input', 'What the planner does'], rows: [['Scenes', 'Places sessions in the order you provide'], ['People', 'Checks each call against an availability window'], ['Rooms', 'Checks the room for the full session'], ['Breaks', 'Adds a planning gap after each scene'], ['Notes', 'Keeps practical context on the printable sheet']] },
144
+ { type: 'title', text: 'How to Read the Timeline', level: 2 },
145
+ { type: 'paragraph', html: 'The timeline uses your order rather than guessing an artistic priority. If a scene starts at 09:00, lasts 60 minutes, and has a 15 minute gap, the next scene starts at 10:15. A conflict means the proposed call does not fit a person or room window; Not scheduled means the session runs past the working day.' },
146
+ { type: 'title', text: 'Check the Draft Before You Send It', level: 2 },
147
+ { type: 'list', items: ['Confirm the order with the director and stage manager.', 'Check travel, access, costume, props, safeguarding, and room reset time.', 'Replace missing availability with a confirmed call before printing.', 'Check local agreements and working time rules with the responsible production team.'] },
148
+ { type: 'title', text: 'A Transparent Draft with Human Judgment Left In', level: 2 },
149
+ { type: 'paragraph', html: 'The planner does not know the best artistic order, travel time, access requirements, safeguarding needs, or local employment rules. It gives the stage manager or director a legible rehearsal call sheet template to challenge with the real production team.' },
150
+ { type: 'tip', title: 'Use availability as a conversation starter', html: 'A missing or conflicting window is not a verdict about a person. It is a prompt to confirm the call, adjust the scene order, change the room, or record the information that is still missing.' },
151
+ { type: 'title', text: 'Print a Rehearsal Call Sheet You Can Use', level: 2 },
152
+ { type: 'paragraph', html: 'Resolve the highlighted calls first, then print the sheet for the room or download the JSON draft for your own production workflow. The page keeps the order, timings, rooms, people, notes, and review status together.' },
153
+ ],
154
+ faq,
155
+ bibliography,
156
+ howTo,
157
+ schemas: [faqSchema, appSchema, howToSchema] as unknown as Record<string, unknown>[],
158
+ };
@@ -0,0 +1,60 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RehearsalCallSheetLocaleContent, RehearsalCallSheetUI } from '../entry';
4
+
5
+ const ui: RehearsalCallSheetUI = {
6
+ productionSection: 'Producción', showName: 'Nombre de la producción', showNamePlaceholder: 'La casa de Bernarda Alba', date: 'Fecha del ensayo',
7
+ timingSection: 'Jornada de trabajo', dayStart: 'Inicio de la jornada', dayEnd: 'Fin de la jornada', breakMinutes: 'Pausa entre escenas',
8
+ sceneSection: 'Escenas y sesiones', scenesLabel: 'Lista de escenas', scenesHint: 'Una línea por escena: Nombre | minutos | sala | personas separadas por comas | nota. Ejemplo: Lucha | 45 | Estudio | Alex, Bea | Trabajo lento.',
9
+ availabilitySection: 'Disponibilidad', participantAvailability: 'Personas', participantHint: 'Una línea por persona: Nombre | primera llamada hasta salida. Los nombres deben coincidir con la lista de escenas.',
10
+ roomAvailability: 'Salas', roomHint: 'Una línea por sala: Sala | hora de apertura hasta hora de cierre. Ejemplo: Estudio | 09:00-18:00.',
11
+ notes: 'Notas de producción', notesPlaceholder: 'Atrezzo, vestuario, accesos o notas de seguridad para toda la jornada',
12
+ presets: 'Empezar con un borrador', presetDay: 'Jornada completa', presetTight: 'Ensayo concentrado', presetShowcase: 'Pase de muestra',
13
+ resultSection: 'Hoja de llamadas', scheduled: 'Programadas', conflicts: 'Conflictos', unscheduled: 'Fuera de jornada', minutes: 'minutos', timelineLabel: 'Línea de tiempo del ensayo', callSheetLabel: 'Hoja de llamadas imprimible',
14
+ statusScheduled: 'Programada', statusConflict: 'Revisar llamada', statusUnscheduled: 'Fuera de jornada', issueDayOverflow: 'Esta sesión termina después de la jornada', issueParticipantAvailability: 'fuera de la disponibilidad de', issueRoomAvailability: 'fuera de la disponibilidad de la sala', issueMissingParticipantAvailability: 'no hay disponibilidad registrada para', issueMissingRoomAvailability: 'no hay disponibilidad registrada para la sala',
15
+ participantSummary: 'Llamadas por persona', roomSummary: 'Uso de salas', scene: 'escena', scenes: 'escenas', firstCall: 'Primera llamada', lastCall: 'Última salida', room: 'Sala', participants: 'Personas', time: 'Hora', status: 'Estado', detail: 'Detalle',
16
+ dateNotSet: 'Fecha sin definir', untitledProduction: 'Producción sin título', noRoom: 'Sin sala indicada', noPeople: 'Sin personas indicadas', readyDetail: 'Lista para ensayar', inputIssuesTitle: 'Revisa estas líneas', inputIssuesHint: 'Estas líneas se han omitido hasta corregirlas.', scenesInput: 'Escenas', peopleInput: 'Disponibilidad de personas', roomsInput: 'Disponibilidad de salas', line: 'línea',
17
+ summaryReady: 'Este borrador está listo para revisarlo con el equipo y llevarlo a imprenta.', summaryAttention: 'Revisa las llamadas marcadas antes de enviar este borrador.', summaryInputIssues: 'Corrige las líneas omitidas antes de confiar en el horario.', noScenes: 'Añade una línea de escena para dibujar la jornada de ensayo.', exportData: 'Descargar JSON', print: 'Imprimir hoja de llamadas', reset: 'Restablecer borrador',
18
+ rulesNote: 'Esto es un borrador de planificación, no una hoja legal de llamadas. Comprueba los acuerdos de producción, las normas del espacio y la normativa laboral local antes de enviarlo.', rulesLinkLabel: 'Consultar la guía británica como referencia',
19
+ };
20
+
21
+ const faq = [
22
+ { question: '¿Qué es una hoja de llamadas de ensayo?', answer: 'Es el plan práctico de una jornada de ensayo: cuándo se trabaja cada escena, quién debe acudir, qué sala se utiliza y qué debe traer o recordar el equipo. Esta herramienta convierte esos datos en un borrador que puedes revisar e imprimir.' },
23
+ { question: '¿Cómo se escribe la lista de escenas?', answer: 'Escribe una escena por línea y separa con barras verticales el nombre, la duración en minutos, la sala, las personas separadas por comas y las notas opcionales. Ejemplo: Lucha | 45 | Estudio | Alex, Bea | Trabajo lento.' },
24
+ { question: '¿Qué significa un conflicto?', answer: 'Significa que una persona o una sala no está disponible durante toda la sesión, o que falta su registro de disponibilidad. La sesión permanece visible para que puedas corregirla en lugar de perderla.' },
25
+ { question: '¿Encuentra la herramienta el horario perfecto?', answer: 'No. Respeta el orden que introduces y comprueba cada sesión contra las ventanas indicadas. No optimiza desplazamientos, contratos, reparto, salas paralelas ni cumplimiento legal.' },
26
+ { question: '¿Dónde se guardan los nombres y las notas?', answer: 'El borrador actual se guarda en este navegador y dispositivo. La herramienta no sube esos datos por sí misma. Usa Restablecer borrador o borra los datos del sitio si quieres eliminarlo.' },
27
+ { question: '¿La pausa hace que la hoja cumpla la ley?', answer: 'No. Solo añade un hueco de planificación entre sesiones. La jornada, los descansos, la protección y las reglas laborales dependen del lugar y de la producción.' },
28
+ ];
29
+
30
+ const howTo = [
31
+ { name: 'Nombra la producción', text: 'Escribe el nombre de la obra o proyecto y la fecha del ensayo para que el documento impreso sea identificable.' },
32
+ { name: 'Define la jornada', text: 'Elige inicio, fin y pausa entre sesiones. Con 15 minutos de pausa, una escena de 60 minutos que empieza a las 09:00 deja la siguiente llamada a las 10:15.' },
33
+ { name: 'Añade las escenas', text: 'Introduce por línea la duración, sala, personas y notas útiles separadas por barras verticales.' },
34
+ { name: 'Añade la disponibilidad', text: 'Indica una ventana para cada persona y sala usando los mismos nombres que en la lista de escenas.' },
35
+ { name: 'Revisa y comparte', text: 'Lee la línea de tiempo, resuelve los conflictos y después imprime la hoja o descarga el borrador JSON.' },
36
+ ];
37
+
38
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
39
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Hoja de llamadas de ensayo', operatingSystem: 'All', applicationCategory: 'BusinessApplication', description: 'Crea un borrador de hoja de llamadas de ensayo con escenas, reparto, salas, disponibilidad y pausas.' };
40
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Cómo crear una hoja de llamadas de ensayo', step: howTo.map((item) => ({ '@type': 'HowToStep', name: item.name, text: item.text })) };
41
+
42
+ export const content: RehearsalCallSheetLocaleContent = {
43
+ slug: 'planificador-hoja-llamada-ensayo-teatro', title: 'Hoja de llamadas de ensayo', description: 'Crea una hoja de llamadas de ensayo para teatro con escenas, reparto, salas, disponibilidad y pausas. Detecta conflictos antes de enviarla.', ui,
44
+ seo: [
45
+ { type: 'title', text: 'Cómo crear una hoja de llamadas de ensayo', level: 2 },
46
+ { type: 'paragraph', html: 'Una hoja de llamadas de ensayo es el plan diario que comparten dirección, regiduría, intérpretes y equipo. Este planificador convierte el orden de tus escenas en un borrador horario, comprueba personas y salas y señala qué debes confirmar antes de enviarlo.' },
47
+ { type: 'title', text: 'Qué debe incluir una buena hoja de llamadas', level: 2 },
48
+ { type: 'paragraph', html: 'No basta con enumerar escenas. Cada sesión necesita duración, sala, personas y una nota si hay que llevar atrezzo, preparar vestuario o proteger una condición de trabajo concreta. Así la conversación de producción se vuelve práctica.' },
49
+ { type: 'table', headers: ['Entrada', 'Qué hace la herramienta'], rows: [['Escenas', 'Coloca las sesiones en el orden indicado'], ['Personas', 'Comprueba cada llamada contra una ventana de disponibilidad'], ['Salas', 'Comprueba la sala durante toda la sesión'], ['Pausas', 'Añade un hueco de planificación entre escenas'], ['Notas', 'Conserva el contexto práctico en la hoja impresa']] },
50
+ { type: 'title', text: 'Cómo leer el horario', level: 2 },
51
+ { type: 'paragraph', html: 'La línea de tiempo respeta tu orden y no inventa prioridades artísticas. Si una escena empieza a las 09:00, dura 60 minutos y tiene una pausa de 15 minutos, la siguiente empieza a las 10:15. Un conflicto indica que una llamada no encaja con la disponibilidad de una persona o sala.' },
52
+ { type: 'title', text: 'Revisa el borrador antes de enviarlo', level: 2 },
53
+ { type: 'list', items: ['Confirma el orden con dirección y regiduría.', 'Comprueba desplazamientos, accesos, vestuario, atrezzo, protección y tiempo de cambio de sala.', 'Sustituye la disponibilidad que falta por una llamada confirmada.', 'Revisa los acuerdos locales y la normativa laboral con el equipo responsable.'] },
54
+ { type: 'title', text: 'Un borrador transparente con criterio humano', level: 2 },
55
+ { type: 'paragraph', html: 'La herramienta no conoce el mejor orden artístico, los desplazamientos, las necesidades de acceso, la protección ni las normas laborales locales. Te entrega una hoja de llamadas de ensayo legible para que el equipo real la revise.' },
56
+ { type: 'tip', title: 'Usa la disponibilidad para abrir una conversación', html: 'Una ventana ausente o conflictiva no es un juicio sobre una persona. Es una señal para confirmar la llamada, cambiar el orden, cambiar de sala o completar la información.' },
57
+ { type: 'title', text: 'Imprime una hoja de llamadas útil', level: 2 },
58
+ { type: 'paragraph', html: 'Resuelve primero las llamadas marcadas y después imprime la hoja para la sala o descarga el borrador JSON. El orden, los horarios, las salas, las personas, las notas y el estado de revisión permanecen juntos.' },
59
+ ], faq, bibliography, howTo, schemas: [faqSchema, appSchema, howToSchema] as unknown as Record<string, unknown>[],
60
+ };
@@ -0,0 +1,60 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RehearsalCallSheetLocaleContent, RehearsalCallSheetUI } from '../entry';
4
+
5
+ const ui: RehearsalCallSheetUI = {
6
+ productionSection: 'Production', showName: 'Nom de la production', showNamePlaceholder: 'Le Misanthrope', date: 'Date de répétition',
7
+ timingSection: 'Journée de travail', dayStart: 'Début de journée', dayEnd: 'Fin de journée', breakMinutes: 'Pause entre les scènes',
8
+ sceneSection: 'Scènes et séances', scenesLabel: 'Liste des scènes', scenesHint: 'Une ligne par scène: Nom | minutes | salle | personnes séparées par des virgules | note. Exemple: Combat | 45 | Studio | Alex, Bea | Travail lent.',
9
+ availabilitySection: 'Disponibilités', participantAvailability: 'Personnes', participantHint: 'Une ligne par personne: Nom | première convocation jusqu au dernier départ. Les noms doivent correspondre à la liste des scènes.',
10
+ roomAvailability: 'Salles', roomHint: 'Une ligne par salle: Salle | heure d ouverture jusqu à heure de fermeture. Exemple: Studio | 09:00-18:00.',
11
+ notes: 'Notes de production', notesPlaceholder: 'Accessoires, costumes, accès ou notes de sécurité pour la journée',
12
+ presets: 'Commencer avec un brouillon', presetDay: 'Journée complète', presetTight: 'Répétition concentrée', presetShowcase: 'Filage de présentation',
13
+ resultSection: 'Feuille de convocation', scheduled: 'Planifiées', conflicts: 'Conflits', unscheduled: 'Hors journée', minutes: 'minutes', timelineLabel: 'Chronologie de la répétition', callSheetLabel: 'Feuille de convocation imprimable',
14
+ statusScheduled: 'Planifiée', statusConflict: 'Vérifier la convocation', statusUnscheduled: 'Hors journée', issueDayOverflow: 'Cette séance dépasse la fin de la journée', issueParticipantAvailability: 'en dehors de la disponibilité de', issueRoomAvailability: 'en dehors de la disponibilité de la salle', issueMissingParticipantAvailability: 'aucune disponibilité indiquée pour', issueMissingRoomAvailability: 'aucune disponibilité indiquée pour la salle',
15
+ participantSummary: 'Convocations par personne', roomSummary: 'Occupation des salles', scene: 'scène', scenes: 'scènes', firstCall: 'Première convocation', lastCall: 'Dernier départ', room: 'Salle', participants: 'Personnes', time: 'Horaire', status: 'Statut', detail: 'Détail',
16
+ dateNotSet: 'Date non définie', untitledProduction: 'Production sans titre', noRoom: 'Aucune salle indiquée', noPeople: 'Aucune personne indiquée', readyDetail: 'Prête pour la répétition', inputIssuesTitle: 'Vérifier ces lignes', inputIssuesHint: 'Ces lignes sont ignorées jusqu à leur correction.', scenesInput: 'Scènes', peopleInput: 'Disponibilité des personnes', roomsInput: 'Disponibilité des salles', line: 'ligne',
17
+ summaryReady: 'Ce brouillon peut être relu avec l équipe puis imprimé.', summaryAttention: 'Vérifie les convocations signalées avant d envoyer ce brouillon.', summaryInputIssues: 'Corrige les lignes ignorées avant de te fier à l horaire.', noScenes: 'Ajoute une ligne de scène pour dessiner la journée de répétition.', exportData: 'Télécharger JSON', print: 'Imprimer la feuille', reset: 'Réinitialiser le brouillon',
18
+ rulesNote: 'Ceci est un brouillon de planification, pas une feuille légale de convocation. Vérifie les accords de production, les règles du lieu et le droit du travail local avant l envoi.', rulesLinkLabel: 'Lire les recommandations britanniques comme référence',
19
+ };
20
+
21
+ const faq = [
22
+ { question: 'Qu est ce qu une feuille de convocation de répétition?', answer: 'C est le plan pratique d une journée de répétition: quand chaque scène est travaillée, qui doit venir, quelle salle est utilisée et ce que l équipe doit apporter ou retenir. Cet outil transforme ces informations en brouillon à vérifier et à imprimer.' },
23
+ { question: 'Comment formater la liste des scènes?', answer: 'Écris une scène par ligne et sépare par des barres verticales le nom, la durée en minutes, la salle, les personnes séparées par des virgules et les notes facultatives. Exemple: Combat | 45 | Studio | Alex, Bea | Travail lent.' },
24
+ { question: 'Que signifie un conflit?', answer: 'Cela signifie qu une personne ou une salle n est pas disponible pendant toute la séance, ou qu une disponibilité manque. La séance reste visible afin que tu puisses la corriger.' },
25
+ { question: 'L outil trouve t il l horaire parfait?', answer: 'Non. Il respecte l ordre fourni et vérifie chaque séance par rapport aux créneaux saisis. Il n optimise pas les déplacements, les contrats, la distribution, les salles parallèles ni la conformité légale.' },
26
+ { question: 'Où sont enregistrés les noms et les notes?', answer: 'Le brouillon est enregistré dans ce navigateur sur cet appareil. Le planificateur ne téléverse pas ces données lui même. Utilise Réinitialiser le brouillon ou efface les données du site pour le supprimer.' },
27
+ { question: 'Le champ de pause rend il la feuille conforme à la loi?', answer: 'Non. Il ajoute seulement un espace de planification entre les séances. Le temps de travail, le repos, la protection et les règles d emploi dépendent du lieu et de la production.' },
28
+ ];
29
+
30
+ const howTo = [
31
+ { name: 'Nommer la production', text: 'Saisis le nom du spectacle ou du projet et la date afin que la feuille imprimée soit identifiable.' },
32
+ { name: 'Définir la journée', text: 'Choisis le début, la fin et la pause entre les séances. Avec 15 minutes de pause, une scène de 60 minutes commençant à 09:00 est suivie d une convocation à 10:15.' },
33
+ { name: 'Ajouter les scènes', text: 'Saisis par ligne la durée, la salle, les personnes et les notes utiles en les séparant par des barres verticales.' },
34
+ { name: 'Ajouter les disponibilités', text: 'Indique un créneau pour chaque personne et chaque salle en utilisant les mêmes noms que dans la liste des scènes.' },
35
+ { name: 'Relire et partager', text: 'Lis la chronologie, résous les conflits, puis imprime la feuille ou télécharge le brouillon JSON.' },
36
+ ];
37
+
38
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
39
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Feuille de convocation de répétition', operatingSystem: 'All', applicationCategory: 'BusinessApplication', description: 'Crée un brouillon de feuille de convocation avec scènes, distribution, salles, disponibilités et pauses.' };
40
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Créer une feuille de convocation de répétition', step: howTo.map((item) => ({ '@type': 'HowToStep', name: item.name, text: item.text })) };
41
+
42
+ export const content: RehearsalCallSheetLocaleContent = {
43
+ slug: 'planificateur-feuille-convocation-repetition-theatre', title: 'Feuille de convocation de répétition', description: 'Crée une feuille de convocation de répétition pour le théâtre avec scènes, distribution, salles, disponibilités et pauses.', ui,
44
+ seo: [
45
+ { type: 'title', text: 'Créer une feuille de convocation de répétition', level: 2 },
46
+ { type: 'paragraph', html: 'Une feuille de convocation de répétition est le plan quotidien partagé par la mise en scène, la régie, les interprètes et l équipe. Ce planificateur transforme l ordre des scènes en brouillon horaire et signale ce qui doit être confirmé avant l envoi.' },
47
+ { type: 'title', text: 'Ce qu une bonne feuille doit contenir', level: 2 },
48
+ { type: 'paragraph', html: 'Une liste utile ne contient pas seulement des titres de scènes. Chaque séance a besoin d une durée, d une salle, des personnes concernées et d une note si des accessoires, costumes, préparations ou conditions de travail particulières sont nécessaires.' },
49
+ { type: 'table', headers: ['Entrée', 'Ce que fait l outil'], rows: [['Scènes', 'Place les séances dans l ordre indiqué'], ['Personnes', 'Vérifie chaque convocation selon un créneau'], ['Salles', 'Vérifie la salle pendant toute la séance'], ['Pauses', 'Ajoute un espace de planification entre les scènes'], ['Notes', 'Conserve le contexte pratique sur la feuille imprimée']] },
50
+ { type: 'title', text: 'Lire la chronologie', level: 2 },
51
+ { type: 'paragraph', html: 'La chronologie respecte ton ordre et ne devine pas de priorité artistique. Si une scène commence à 09:00, dure 60 minutes et prévoit 15 minutes de pause, la suivante commence à 10:15. Un conflit indique qu une personne ou une salle ne correspond pas au créneau.' },
52
+ { type: 'title', text: 'Relire le brouillon avant l envoi', level: 2 },
53
+ { type: 'list', items: ['Confirme l ordre avec la mise en scène et la régie.', 'Vérifie les déplacements, les accès, les costumes, les accessoires, la protection et le temps de remise en état.', 'Remplace les disponibilités manquantes par une convocation confirmée.', 'Relis les accords locaux et les règles de temps de travail avec l équipe responsable.'] },
54
+ { type: 'title', text: 'Un brouillon transparent avec un jugement humain', level: 2 },
55
+ { type: 'paragraph', html: 'L outil ne connaît ni le meilleur ordre artistique, ni les déplacements, les besoins d accès, la protection ou les règles locales. Il fournit une feuille lisible que la vraie équipe de production peut relire ensemble.' },
56
+ { type: 'tip', title: 'Utiliser la disponibilité pour ouvrir le dialogue', html: 'Une disponibilité manquante ou contradictoire ne juge pas une personne. Elle indique qu il faut confirmer la convocation, modifier l ordre, changer de salle ou compléter l information.' },
57
+ { type: 'title', text: 'Imprimer une feuille vraiment utilisable', level: 2 },
58
+ { type: 'paragraph', html: 'Résous d abord les convocations signalées, puis imprime la feuille pour la salle ou télécharge le brouillon JSON. L ordre, les horaires, les salles, les personnes, les notes et le statut de vérification restent réunis.' },
59
+ ], faq, bibliography, howTo, schemas: [faqSchema, appSchema, howToSchema] as unknown as Record<string, unknown>[],
60
+ };
@@ -0,0 +1,47 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import { bibliography } from '../bibliography';
3
+ import type { RehearsalCallSheetLocaleContent, RehearsalCallSheetUI } from '../entry';
4
+
5
+ const ui: RehearsalCallSheetUI = {
6
+ productionSection: 'Produksi', showName: 'Nama produksi', showNamePlaceholder: 'Lakon panggung', date: 'Tanggal latihan', timingSection: 'Hari kerja', dayStart: 'Mulai hari', dayEnd: 'Selesai hari', breakMinutes: 'Jeda antaradegan',
7
+ sceneSection: 'Adegan dan sesi', scenesLabel: 'Daftar adegan', scenesHint: 'Satu baris per adegan: Nama | menit | ruangan | orang dipisahkan koma | catatan. Contoh: Latihan laga | 45 | Studio | Alex, Bea | Kerja perlahan.', availabilitySection: 'Ketersediaan', participantAvailability: 'Orang', participantHint: 'Satu baris per orang: Nama | panggilan pertama sampai pulang terakhir. Nama harus sama dengan daftar adegan.', roomAvailability: 'Ruangan', roomHint: 'Satu baris per ruangan: Ruangan | waktu buka sampai tutup. Contoh: Studio | 09:00-18:00.',
8
+ notes: 'Catatan produksi', notesPlaceholder: 'Properti, kostum, akses, atau catatan keselamatan untuk hari ini', presets: 'Mulai dengan draf', presetDay: 'Latihan sehari penuh', presetTight: 'Latihan singkat', presetShowcase: 'Pentas uji coba',
9
+ resultSection: 'Lembar panggilan', scheduled: 'Terjadwal', conflicts: 'Konflik', unscheduled: 'Tidak terjadwal', minutes: 'menit', timelineLabel: 'Linimasa latihan', callSheetLabel: 'Lembar panggilan latihan yang dapat dicetak', statusScheduled: 'Terjadwal', statusConflict: 'Periksa panggilan', statusUnscheduled: 'Tidak terjadwal', issueDayOverflow: 'Sesi ini melewati akhir hari kerja', issueParticipantAvailability: 'di luar ketersediaan', issueRoomAvailability: 'di luar ketersediaan ruangan', issueMissingParticipantAvailability: 'belum ada ketersediaan untuk', issueMissingRoomAvailability: 'belum ada ketersediaan untuk ruangan',
10
+ participantSummary: 'Panggilan per orang', roomSummary: 'Penggunaan ruangan', scene: 'adegan', scenes: 'adegan', firstCall: 'Panggilan pertama', lastCall: 'Pulang terakhir', room: 'Ruangan', participants: 'Orang', time: 'Waktu', status: 'Status', detail: 'Detail', dateNotSet: 'Tanggal belum ditentukan', untitledProduction: 'Produksi tanpa judul', noRoom: 'Ruangan belum diisi', noPeople: 'Orang belum diisi', readyDetail: 'Siap untuk latihan', inputIssuesTitle: 'Periksa baris berikut', inputIssuesHint: 'Baris ini dilewati sampai diperbaiki.', scenesInput: 'Adegan', peopleInput: 'Ketersediaan orang', roomsInput: 'Ketersediaan ruangan', line: 'baris',
11
+ summaryReady: 'Draf ini siap ditinjau bersama tim produksi dan dicetak.', summaryAttention: 'Periksa panggilan yang ditandai sebelum mengirim draf ini.', summaryInputIssues: 'Perbaiki baris yang dilewati sebelum mengandalkan jadwal ini.', noScenes: 'Tambahkan baris adegan untuk menggambar jadwal latihan.', exportData: 'Unduh JSON', print: 'Cetak lembar panggilan', reset: 'Atur ulang draf', rulesNote: 'Ini adalah draf perencanaan, bukan lembar panggilan resmi atau dokumen hukum. Periksa kesepakatan produksi, aturan tempat, dan ketentuan kerja setempat sebelum mengirimnya.', rulesLinkLabel: 'Baca panduan Inggris sebagai referensi',
12
+ };
13
+
14
+ const faq = [
15
+ { question: 'Apa itu lembar panggilan latihan?', answer: 'Ini adalah rencana praktis untuk satu hari latihan: kapan setiap adegan dikerjakan, siapa yang hadir, ruangan yang dipakai, dan apa yang perlu dibawa atau diingat tim. Perencana ini mengubah informasi itu menjadi draf yang dapat diperiksa dan dicetak.' },
16
+ { question: 'Bagaimana format daftar adegan?', answer: 'Tulis satu adegan per baris dan pisahkan nama, durasi dalam menit, ruangan, orang yang dipisahkan koma, serta catatan opsional dengan tanda garis vertikal. Contoh: Latihan laga | 45 | Studio | Alex, Bea | Kerja perlahan.' },
17
+ { question: 'Apa arti konflik?', answer: 'Konflik berarti seseorang atau ruangan tidak tersedia selama seluruh sesi, atau catatan ketersediaannya belum ada. Sesi tetap ditampilkan agar dapat diperbaiki.' },
18
+ { question: 'Apakah perencana ini menemukan jadwal sempurna?', answer: 'Tidak. Perencana mengikuti urutan yang kamu masukkan dan memeriksa setiap sesi terhadap jendela waktu. Perjalanan, kontrak, pemeran, ruangan paralel, dan kepatuhan hukum tidak dioptimalkan.' },
19
+ { question: 'Di mana nama dan catatan disimpan?', answer: 'Draf saat ini disimpan di browser pada perangkat ini. Perencana tidak mengunggah data tersebut. Gunakan Atur ulang draf atau hapus data situs jika ingin menghapusnya.' },
20
+ { question: 'Apakah kolom jeda membuat panggilan sesuai hukum?', answer: 'Tidak. Kolom itu hanya menambahkan jeda perencanaan antarsesi. Waktu kerja, istirahat, perlindungan, dan aturan ketenagakerjaan berbeda menurut tempat dan produksi.' },
21
+ ];
22
+
23
+ const howTo = [
24
+ { name: 'Beri nama produksi', text: 'Masukkan nama pertunjukan atau proyek dan tanggal latihan agar lembar cetak mudah dikenali.' },
25
+ { name: 'Atur hari kerja', text: 'Pilih waktu mulai, selesai, dan jeda antarsesi. Dengan jeda 15 menit, adegan 60 menit yang mulai pukul 09:00 diikuti panggilan pukul 10:15.' },
26
+ { name: 'Masukkan adegan', text: 'Tambahkan durasi, ruangan, orang, dan catatan berguna pada setiap baris.' },
27
+ { name: 'Tambahkan ketersediaan', text: 'Berikan jendela waktu untuk setiap orang dan ruangan menggunakan nama yang sama seperti di daftar adegan.' },
28
+ { name: 'Periksa dan bagikan', text: 'Baca linimasa, selesaikan konflik, lalu cetak lembar panggilan atau unduh draf JSON.' },
29
+ ];
30
+
31
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
32
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Perencana lembar panggilan latihan', operatingSystem: 'All', applicationCategory: 'BusinessApplication', description: 'Buat draf lembar panggilan latihan dari adegan, pemeran, ruangan, ketersediaan, dan jeda.' };
33
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Cara membuat lembar panggilan latihan', step: howTo.map((item) => ({ '@type': 'HowToStep', name: item.name, text: item.text })) };
34
+
35
+ export const content: RehearsalCallSheetLocaleContent = {
36
+ slug: 'perencana-lembar-panggilan-latihan-teater', title: 'Lembar panggilan latihan', description: 'Buat lembar panggilan latihan teater dari adegan, pemeran, ruangan, ketersediaan, dan jeda. Temukan konflik sebelum mengirimnya.', ui,
37
+ seo: [
38
+ { type: 'title', text: 'Cara membuat lembar panggilan latihan', level: 2 }, { type: 'paragraph', html: 'Lembar panggilan latihan adalah rencana harian yang dipakai sutradara, manajer panggung, pemeran, dan kru. Perencana ini mengubah urutan adegan menjadi draf waktu, memeriksa orang dan ruangan, lalu menunjukkan hal yang perlu dikonfirmasi sebelum dikirim.' },
39
+ { type: 'title', text: 'Isi lembar panggilan yang berguna', level: 2 }, { type: 'paragraph', html: 'Daftar yang berguna bukan hanya kumpulan judul adegan. Setiap sesi memerlukan durasi, ruangan, orang yang terlibat, serta catatan jika ada properti, kostum, persiapan, atau kondisi kerja tertentu.' },
40
+ { type: 'table', headers: ['Masukan', 'Yang dilakukan perencana'], rows: [['Adegan', 'Menempatkan sesi sesuai urutanmu'], ['Orang', 'Memeriksa setiap panggilan terhadap jendela waktu'], ['Ruangan', 'Memeriksa ruangan selama seluruh sesi'], ['Jeda', 'Menambahkan jarak perencanaan antaradegan'], ['Catatan', 'Menyimpan konteks praktis pada lembar cetak']] },
41
+ { type: 'title', text: 'Cara membaca linimasa', level: 2 }, { type: 'paragraph', html: 'Linimasa mengikuti urutanmu dan tidak menebak prioritas artistik. Jika adegan mulai pukul 09:00, berlangsung 60 menit, dan memiliki jeda 15 menit, adegan berikutnya mulai pukul 10:15. Konflik berarti panggilan tidak sesuai dengan jendela orang atau ruangan.' },
42
+ { type: 'title', text: 'Periksa draf sebelum mengirim', level: 2 }, { type: 'list', items: ['Konfirmasikan urutan dengan sutradara dan manajer panggung.', 'Periksa perjalanan, akses, kostum, properti, perlindungan, dan waktu menata ulang ruangan.', 'Ganti ketersediaan yang kosong dengan panggilan yang sudah dikonfirmasi.', 'Periksa kesepakatan setempat dan aturan waktu kerja bersama tim yang bertanggung jawab.'] },
43
+ { type: 'title', text: 'Draf transparan dengan penilaian manusia', level: 2 }, { type: 'paragraph', html: 'Perencana ini tidak mengetahui urutan artistik terbaik, perjalanan, kebutuhan akses, perlindungan, atau aturan kerja setempat. Hasilnya adalah lembar panggilan yang mudah dibaca untuk ditinjau tim produksi.' },
44
+ { type: 'tip', title: 'Jadikan ketersediaan sebagai awal percakapan', html: 'Jendela yang hilang atau bertentangan bukan penilaian terhadap seseorang. Itu adalah tanda untuk mengonfirmasi panggilan, mengubah urutan, mengganti ruangan, atau melengkapi informasi.' },
45
+ { type: 'title', text: 'Cetak lembar panggilan yang siap dipakai', level: 2 }, { type: 'paragraph', html: 'Selesaikan panggilan yang ditandai, lalu cetak lembar untuk ruangan atau unduh draf JSON. Urutan, waktu, ruangan, orang, catatan, dan status pemeriksaan tetap berada dalam satu dokumen.' },
46
+ ], faq, bibliography, howTo, schemas: [faqSchema, appSchema, howToSchema] as unknown as Record<string, unknown>[],
47
+ };