Initial commit: Filebot Media Browser
Vue 3 + FastAPI Webapp zur Anzeige von Filebot/n8n-Medienmetadaten aus PostgreSQL. Enthält Frontend, API, Nginx-Config, SQL-Migrationen und Docker-Compose-Setup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
export interface DateTimeBound {
|
||||
date: string
|
||||
hour: string
|
||||
minute: string
|
||||
}
|
||||
|
||||
export const emptyDateTimeBound = (): DateTimeBound => ({
|
||||
date: '',
|
||||
hour: '00',
|
||||
minute: '00',
|
||||
})
|
||||
|
||||
export function boundToIso(bound: DateTimeBound): string | undefined {
|
||||
if (!bound.date) return undefined
|
||||
const local = new Date(
|
||||
`${bound.date}T${bound.hour.padStart(2, '0')}:${bound.minute.padStart(2, '0')}:00`
|
||||
)
|
||||
if (Number.isNaN(local.getTime())) return undefined
|
||||
return local.toISOString()
|
||||
}
|
||||
|
||||
export function formatBoundLabel(bound: DateTimeBound): string {
|
||||
if (!bound.date) return 'Datum wählen'
|
||||
return `${formatDateDe(bound.date)} ${bound.hour.padStart(2, '0')}:${bound.minute.padStart(2, '0')}`
|
||||
}
|
||||
|
||||
export function formatDateDe(isoDate: string): string {
|
||||
const [y, m, d] = isoDate.split('-').map(Number)
|
||||
if (!y || !m || !d) return isoDate
|
||||
return new Date(y, m - 1, d).toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
export function pad2(n: number): string {
|
||||
return String(n).padStart(2, '0')
|
||||
}
|
||||
|
||||
export const HOURS = Array.from({ length: 24 }, (_, i) => pad2(i))
|
||||
export const MINUTES = Array.from({ length: 60 }, (_, i) => pad2(i))
|
||||
|
||||
const WEEKDAYS = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']
|
||||
const MONTHS = [
|
||||
'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
|
||||
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember',
|
||||
]
|
||||
|
||||
export function getWeekdayLabels(): string[] {
|
||||
return WEEKDAYS
|
||||
}
|
||||
|
||||
export function getMonthLabel(monthIndex: number): string {
|
||||
return MONTHS[monthIndex] ?? ''
|
||||
}
|
||||
|
||||
/** Kalendertage inkl. Auffüllung für Mo-start Grid */
|
||||
export function getCalendarDays(year: number, month: number) {
|
||||
const first = new Date(year, month, 1)
|
||||
const last = new Date(year, month + 1, 0)
|
||||
let startPad = first.getDay() - 1
|
||||
if (startPad < 0) startPad = 6
|
||||
|
||||
const days: { date: string; day: number; currentMonth: boolean }[] = []
|
||||
|
||||
for (let i = startPad - 1; i >= 0; i--) {
|
||||
const d = new Date(year, month, -i)
|
||||
days.push({
|
||||
date: toIsoDate(d),
|
||||
day: d.getDate(),
|
||||
currentMonth: false,
|
||||
})
|
||||
}
|
||||
|
||||
for (let d = 1; d <= last.getDate(); d++) {
|
||||
days.push({
|
||||
date: toIsoDate(new Date(year, month, d)),
|
||||
day: d,
|
||||
currentMonth: true,
|
||||
})
|
||||
}
|
||||
|
||||
const totalCells = Math.ceil((startPad + last.getDate()) / 7) * 7
|
||||
let nextDay = 1
|
||||
while (days.length < totalCells) {
|
||||
const d = new Date(year, month + 1, nextDay++)
|
||||
days.push({
|
||||
date: toIsoDate(d),
|
||||
day: d.getDate(),
|
||||
currentMonth: false,
|
||||
})
|
||||
}
|
||||
|
||||
return days
|
||||
}
|
||||
|
||||
export function toIsoDate(d: Date): string {
|
||||
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`
|
||||
}
|
||||
|
||||
export function isBoundActive(bound: DateTimeBound): boolean {
|
||||
return Boolean(bound.date)
|
||||
}
|
||||
Reference in New Issue
Block a user