- Preview
- Code
- Data (.dayta)
Simple Data Report (one log per row). Project totals in cards.
Copy
import { Page, Text, View, Document, StyleSheet, Image } from '@react-pdf/renderer';
import { useDaytalog } from 'daytalog';
import logo from './assets/obsidian.png';
const DataReport: React.FC = () => {
const { projectName, logs, total } = useDaytalog();
const rows: Array<Record<string, any>> = logs.map((log) => ({
index: log.id,
day: log.day(),
date: log.date(),
duration: log.ocf.duration(),
ocf_size: log.ocf.size(),
proxy_size: log.proxy.size(),
audio_size: log.sound.size(),
reels: log.ocf.reels({ mergeRanges: true }),
copies: log.ocf
.copies()
.map(
(vol, index) =>
`${vol.count[0] === vol.count[1] ? '[OK] ' : '[NOT COMPLETE]'}Copy ${index + 1}: ${vol.volumes}\n`,
),
}));
const headers = Object.keys(rows[0] || {});
const shootingDays = () => {
const [start, end] = total.dateRange();
return start === end ? start : `${start} - ${end}`;
};
return (
<Document title='Clips'>
<Page size='A4' orientation='landscape' style={styles.page}>
<View>
<View style={styles.header}>
<View>
<Text style={styles.title}>Data Report</Text>
<Text style={styles.projectInfo}>Project: {projectName}</Text>
<Text style={styles.projectInfo}>Shooting Dates: {shootingDays()}</Text>
</View>
<Image style={styles.logo} src={logo} />
</View>
<View style={styles.cardGroup}>
<View style={styles.card}>
<Text style={styles.cardTitle}>Total Duration</Text>
<Text style={styles.cardText}>{total.ocf.duration()}</Text>
</View>
<View style={styles.card}>
<Text style={styles.cardTitle}>Total OCF Size</Text>
<Text style={styles.cardText}>{total.ocf.size()}</Text>
</View>
<View style={styles.card}>
<Text style={styles.cardTitle}>Total Proxy Size</Text>
<Text style={styles.cardText}>{total.proxy.size()}</Text>
</View>
<View style={styles.card}>
<Text style={styles.cardTitle}>Total Sound Size</Text>
<Text style={styles.cardText}>{total.sound.size()}</Text>
</View>
</View>
<View style={styles.table}>
{/* Header Row */}
<View style={styles.row} fixed>
{headers.map((header, i) => (
<Text
key={i}
style={[
styles.headerCell,
i === 0 && { borderTopLeftRadius: 4 },
i === headers.length - 1 && { borderTopRightRadius: 4 },
]}
>
{header.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())}
</Text>
))}
</View>
{rows.map((row, rowIndex) => (
<View
key={rowIndex}
style={[styles.row, rowIndex % 2 === 1 ? styles.zebra : {}]}
wrap={false}
>
{headers.map((header, colIndex) => (
<Text key={colIndex} style={styles.cell}>
{row[header]}
</Text>
))}
</View>
))}
</View>
</View>
<Text
style={styles.pageNumber}
render={({ pageNumber, totalPages }) => totalPages > 1 && `${pageNumber} / ${totalPages}`}
fixed
/>
</Page>
</Document>
);
};
const styles = StyleSheet.create({
page: { fontFamily: 'Helvetica', padding: 40, fontSize: 12, backgroundColor: '#fff' },
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, color: '#333' },
projectInfo: { fontSize: 12 },
table: { display: 'table', width: '100%', borderCollapse: 'collapse', marginTop: 10 },
row: { flexDirection: 'row' },
headerCell: {
flex: 1,
padding: 6,
backgroundColor: '#000',
color: '#fff',
fontSize: 5,
fontWeight: 'bold',
textAlign: 'left',
},
cell: {
flex: 1,
padding: 6,
fontSize: 4,
color: '#333',
textAlign: 'left',
borderBottomWidth: 0.2,
borderBottomColor: '#ddd',
},
zebra: { backgroundColor: '#f8f8f8' },
pageNumber: { fontSize: 4, position: 'absolute', bottom: 20, right: 20 },
logo: {
width: 60,
height: 60,
marginBottom: 20,
},
header: {
flex: 'auto',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
cardGroup: {
flexDirection: 'row',
gap: 8,
marginTop: 8,
marginBottom: 8,
},
card: {
width: '25%',
gap: 2,
padding: 18,
borderRadius: 8,
borderWidth: 1,
borderColor: '#dedede',
},
cardTitle: {
fontSize: 8,
fontWeight: 'bold',
},
cardText: {
fontSize: 14,
fontWeight: '200',
},
});
export default DataReport;
Copy
day: 1
date: 2025-08-15
ocf:
clips:
- clip: A001C001
size: 7572467904
copies:
- volume: Raid
hash: 4607f64fcae33728
- volume: Master_01
hash: 4607f64fcae33728
- volume: Backup_01
hash: 4607f64fcae33728
tc_start: 10:13:25:08
tc_end: 10:14:01:13
duration: 00:00:36:05
camera_model: CinemaCamera
reel: A001
fps: 25
sensor_fps: 25
lens: 40 mm
shutter: 180
resolution: 4608x3164
codec: ProRes 4444
gamma: LOG-C
ei: 800
wb: 5300
tint: "+0.0"
lut: commercial.cube
- clip: A001C002
size: 8642912484
copies:
- volume: Raid
hash: b90261cadf8aab43
- volume: Master_01
hash: b90261cadf8aab43
- volume: Backup_01
hash: b90261cadf8aab43
tc_start: 10:15:18:07
tc_end: 10:15:59:04
duration: 00:00:40:22
camera_model: CinemaCamera
reel: A001
fps: 25
sensor_fps: 25
lens: 40 mm
shutter: 180
resolution: 4608x3164
codec: ProRes 4444
gamma: LOG-C
ei: 800
wb: 5300
tint: "+0.0"
lut: commercial.cube
- clip: A001C003
size: 13965590828
copies:
- volume: Raid
hash: d21f12407d0ca819
- volume: Master_01
hash: d21f12407d0ca819
- volume: Backup_01
hash: d21f12407d0ca819
tc_start: 10:16:28:14
tc_end: 10:17:35:14
duration: 00:01:07:00
camera_model: CinemaCamera
reel: A001
fps: 25
sensor_fps: 25
lens: 40 mm
shutter: 180
resolution: 4608x3164
codec: ProRes 4444
gamma: LOG-C
ei: 800
wb: 5300
tint: "+0.0"
lut: commercial.cube
sound:
clips:
- clip: Clip001
size: 43926156
copies:
- volume: Raid
hash: b69d17d38b5fcb32
- volume: Master_01
hash: b69d17d38b5fcb32
- volume: Backup_01
hash: b69d17d38b5fcb32
tc_start: 10:13:07:00
tc_end: 10:14:08:00
- clip: Clip002
size: 38886174
copies:
- volume: Raid
hash: 2157d011ed699744
- volume: Master_01
hash: 2157d011ed699744
- volume: Backup_01
hash: 2157d011ed699744
tc_start: 10:15:09:00
tc_end: 10:16:03:00
- clip: Clip003
size: 57606144
copies:
- volume: Raid
hash: 8f520e1bed7eec41
- volume: Master_01
hash: 8f520e1bed7eec41
- volume: Backup_01
hash: 8f520e1bed7eec41
tc_start: 10:16:18:00
tc_end: 10:17:38:00
proxy:
clips:
- clip: A001C001
size: 159900258
format: MOV
codec: prores
resolution: 1920x1080
- clip: A001C002
size: 186713854
format: MOV
codec: prores
resolution: 1920x1080
- clip: A001C003
size: 305947102
format: MOV
codec: prores
resolution: 1920x1080
version: 1