Files
2026-10-08 09:03:15 +02:00

598 lines
16 KiB
TypeScript

export type WorkType = "support" | "consulting";
export type BillingStatus = "billable" | "billed" | "non_billable" | null;
export type PeriodType = "month" | "day";
export type TicketDetailPeriodType = PeriodType | "all";
export type UserRole = "admin" | "user";
export type AuthUser = {
id: string;
username: string;
display_name: string;
role: UserRole;
active: boolean;
zammad_user_id: string | null;
has_zammad_api_token: boolean;
teamspace_user_id: string | null;
has_teamspace_credentials: boolean;
zammad_email_session_webhook_enabled: boolean;
session_billing_prompt_enabled: boolean;
teamspace_time_tracking_enabled: boolean;
zammad_session_note_default_enabled: boolean;
zammad_time_unit_rounding_enabled: boolean;
zammad_time_unit_rounding_minutes: number;
zammad_atoss_email_sync_enabled: boolean;
zammad_article_type_id: number;
project_followup_enabled: boolean;
project_followup_weeks: number;
project_followup_second_enabled: boolean;
project_followup_second_weeks: number;
project_followup_complete_on_session: boolean;
};
export type DashboardWidgetType = "dayControl" | "today" | "month" | "openRatings" | "projectBudgets" | "todos" | "teamspace" | "favorites" | "divider" | "statistic" | "notes";
export type DashboardSortDirection = "asc" | "desc";
export type DashboardFavorite = {
ticketNumber: string;
addedAt: string;
sortOrder: number;
};
export type DashboardWidget = {
id: string;
type: DashboardWidgetType;
title?: string;
x: number;
y: number;
w: number;
h: number;
config?: Record<string, unknown>;
};
export type DashboardPreference = {
version: number;
widgets: DashboardWidget[];
favorites: DashboardFavorite[];
};
export type DashboardDayTicket = {
ticket_id: string;
ticket_number: string;
title: string | null;
organization_name: string | null;
work_type: WorkType | null;
zammad_priority_id: string | null;
zammad_state_id: string | null;
zammad_pending_time: string | null;
latest_started_at: string | null;
has_project: boolean;
own_session_count: number;
open_session_count: number;
open_todo_count: number;
due_todo_count: number;
};
export type AdminUser = AuthUser & {
created_at: string;
updated_at: string;
};
export type ZammadSessionArticleQueueItem = {
id: string;
session_id: string;
ticket_id: string;
ticket_number: string;
zammad_ticket_id: string | null;
activity: string;
time_unit: number;
zammad_article_type_id: number;
attempt_count: number;
last_error: string | null;
last_attempt_at: string | null;
created_at: string;
started_at: string;
rounded_minutes: number;
};
export type Organization = {
id: string;
zammad_id: string;
name: string;
synced_at: string;
};
export type RecurringBillingSlot = {
id?: string;
weekday: number | null;
start_time: string | null;
duration_minutes: number;
};
export type RecurringBilling = {
id: string;
user_id: string;
owner_username: string;
owner_display_name: string;
ticket_number: string;
configured_ticket_number: string | null;
organization_id: string;
organization_name: string;
activity: string;
work_type: WorkType;
recurrence_type: "weekly" | "every_n_weeks";
interval_value: number;
valid_from: string;
valid_until: string | null;
start_time: string;
active: boolean;
created_at: string;
updated_at: string;
slots: RecurringBillingSlot[];
};
export type TicketMeta = {
id: string;
ticket_number: string;
title: string | null;
organization_id: string | null;
organization_name: string | null;
customer_name: string | null;
work_type: WorkType | null;
zammad_ticket_id?: string | null;
zammad_group_id?: string | null;
zammad_priority_id?: string | null;
zammad_state_id?: string | null;
zammad_owner_id?: string | null;
zammad_owner_user_id?: string | null;
zammad_owner_display_name?: string | null;
zammad_owner_username?: string | null;
is_foreign_zammad_owner?: boolean;
can_edit_ticket_data?: boolean;
project_id?: string | null;
is_project?: boolean;
project_name?: string | null;
project_hour_budget_minutes?: number | null;
project_teamspace_entry_count?: number;
project_teamspace_entries?: ProjectTeamspaceEntry[];
project_owner_user_id?: string | null;
project_owner_display_name?: string | null;
can_edit_project?: boolean;
has_recurring_billing?: boolean;
};
export type TicketSearchResult = {
ticket_id: string;
ticket_number: string;
title: string | null;
organization_id: string | null;
organization_name: string | null;
customer_name: string | null;
work_type: WorkType | null;
project_id: string | null;
is_project: boolean;
project_name: string | null;
latest_started_at: string | null;
latest_period: string;
session_count: number;
total_minutes: number;
};
export type ProjectSummary = {
id: string;
user_id?: string;
owner_username?: string | null;
owner_display_name?: string | null;
can_edit?: boolean;
ticket_id: string;
ticket_number: string;
title: string | null;
organization_id: string | null;
organization_name: string | null;
customer_name: string | null;
work_type: WorkType | null;
zammad_priority_id?: string | null;
zammad_state_id?: string | null;
zammad_ticket_id?: string | null;
zammad_owner_id?: string | null;
ticket_zammad_owner_id?: string | null;
zammad_pending_time?: string | null;
zammad_merged_from_ticket_id?: string | null;
zammad_merged_from_ticket_number?: string | null;
teamspace_project: string | null;
description: string;
goals: string;
starts_on: string | null;
ends_on: string | null;
hour_budget_minutes: number | null;
manual_consumption_minutes: number;
teamspace_consumption_minutes?: number;
consumption_minutes?: number;
closed_at: string | null;
created_at: string;
updated_at: string;
session_count?: number;
recurring_session_count?: number;
manual_session_count?: number;
has_recurring_billing?: boolean;
billed_minutes?: number;
open_todos?: number;
overdue_todos?: number;
done_todos?: number;
teamspace_entry_count?: number;
teamspace_entries?: ProjectTeamspaceEntry[];
unassigned_teamspace_session_count?: number;
unassigned_teamspace_subentry_count?: number;
close_review_required?: boolean;
hidden_for_user?: boolean;
};
export type ProjectTeamspaceEntry = {
id: string;
project_id?: string;
teamspace_project: string | null;
teamspace_api_project_id?: string | null;
teamspace_api_project_url?: string | null;
teamspace_description?: string | null;
subentries?: ProjectTeamspaceSubentry[];
is_default: boolean;
hour_budget_minutes: number | null;
budget_sync_disabled?: boolean;
manual_consumption_minutes: number;
teamspace_consumption_minutes?: number;
consumption_minutes?: number;
display_order?: number;
};
export type ProjectTeamspaceSubentry = {
id: string;
project_teamspace_entry_id?: string;
teamspace_api_project_id: string;
teamspace_api_project_url: string | null;
teamspace_project_id: string | null;
billing_type: string | null;
name: string | null;
alias: string;
description: string;
progress: number | null;
planned_minutes: number | null;
sum_minutes: number | null;
planned_minutes_billable: number | null;
sum_minutes_billable: number | null;
planned_income: number | string | null;
sum_income: number | string | null;
consumption_minutes?: number;
display_order: number;
};
export type TodoPriority = "low" | "medium" | "high" | "urgent";
export type TodoStatus = "open" | "done";
export type TicketTodo = {
id: string;
ticket_id: string;
ticket_number?: string;
organization_name?: string | null;
project_id?: string | null;
teamspace_project?: string | null;
title: string;
description: string;
priority: TodoPriority;
status: TodoStatus;
sort_order: number;
starts_on: string | null;
ends_on: string | null;
completed_at: string | null;
created_at: string;
updated_at: string;
};
export type TicketSummary = {
id: string;
ticket_number: string;
title: string | null;
organization_id: string | null;
organization_name: string | null;
customer_name: string | null;
work_type: WorkType | null;
project_id: string | null;
session_count: number;
total_minutes: number;
crm_billed_minutes: number;
billable_minutes: number;
non_billable_minutes: number;
open_minutes: number;
open_count: number;
ticket_open_count: number;
billable_count: number;
billed_count: number;
non_billable_count: number;
recurring_session_count: number;
manual_session_count: number;
requires_closure: boolean;
closed: boolean;
closed_at: string | null;
};
export type OpenSession = {
id: string;
ticket_id: string;
ticket_number: string;
organization_id: string | null;
organization_name: string | null;
customer_name: string;
activity: string;
work_type: WorkType;
project_teamspace_entry_id?: string | null;
project_teamspace_project?: string | null;
started_at: string;
rounded_minutes: number;
recurring_billing_id?: string | null;
};
export type ActivityBucket = {
bucket_key: string;
session_count: number;
total_minutes: number;
};
export type PeriodOverview = {
periodType: PeriodType;
period: string;
closed: boolean;
closedAt: string | null;
canClose: boolean;
totals: {
tickets: number;
sessions: number;
minutes: number;
crmBilledMinutes: number;
billableMinutes: number;
nonBillableMinutes: number;
openMinutes: number;
openSessions: number;
};
tickets: TicketSummary[];
openSessions: OpenSession[];
activitySeries: ActivityBucket[];
};
export type TicketDayBilling = {
day: string;
project_teamspace_entry_id?: string | null;
billed_minutes: number;
};
export type TicketAvailableMonth = {
month: string;
session_count: number;
};
export type SessionEntry = {
id: string;
organization_id: string | null;
organization_name: string | null;
customer_name: string;
activity: string;
zammad_article_body?: string | null;
work_type: WorkType;
project_teamspace_entry_id?: string | null;
project_teamspace_project?: string | null;
started_at: string;
ended_at: string;
duration_seconds: number;
rounded_minutes: number;
billing_status: BillingStatus;
billing_updated_at: string | null;
created_at: string;
recurring_billing_id: string | null;
recurring_billing_slot_id: string | null;
recurring_occurrence_date: string | null;
zammad_source_article_id?: string | null;
zammad_article_status?: "pending" | "sent" | null;
zammad_article_id?: string | null;
zammad_article_type_id?: number;
};
export type TicketPeriod = {
periodType: TicketDetailPeriodType;
period: string;
closed: boolean;
closedAt: string | null;
filters?: {
month: string;
status: "all" | "open" | "billable" | "billed" | "non_billable";
source: "all" | "manual" | "recurring";
};
availableMonths?: TicketAvailableMonth[];
ticket: {
id: string;
ticket_number: string;
title: string | null;
organization_id: string | null;
organization_name: string | null;
customer_name: string | null;
work_type: WorkType | null;
zammad_ticket_id?: string | null;
zammad_group_id?: string | null;
zammad_priority_id?: string | null;
zammad_state_id?: string | null;
zammad_owner_id?: string | null;
zammad_owner_user_id?: string | null;
zammad_owner_display_name?: string | null;
zammad_owner_username?: string | null;
is_foreign_zammad_owner?: boolean;
can_edit_ticket_data?: boolean;
closed_at: string | null;
requires_closure: boolean;
recurring_session_count: number;
manual_session_count: number;
has_recurring_billing?: boolean;
};
sessions: SessionEntry[];
dayBillings: TicketDayBilling[];
project?: ProjectSummary | null;
todos?: TicketTodo[];
canClose: boolean;
openCount: number;
ticketOpenCount: number;
};
export type MonthOverview = PeriodOverview;
export type TicketMonth = TicketPeriod;
export type StatisticsDailyBucket = {
day: string;
sessions: number;
total_minutes: number;
billed_minutes: number;
billable_minutes: number;
non_billable_minutes: number;
open_minutes: number;
billed_sessions: number;
billable_sessions: number;
non_billable_sessions: number;
open_sessions: number;
crm_billed_minutes: number;
};
export type StatisticsGroup = {
organization_id?: string | null;
organization_name?: string;
work_type?: WorkType;
tickets: number;
sessions: number;
total_minutes: number;
billed_minutes: number;
billable_minutes: number;
non_billable_minutes: number;
open_minutes: number;
billed_sessions: number;
billable_sessions: number;
non_billable_sessions: number;
open_sessions: number;
crm_billed_minutes: number;
crm_delta_minutes: number;
};
export type StatisticsTicket = {
ticket_id: string;
ticket_number: string;
organization_id: string | null;
organization_name: string;
active_days: number;
sessions: number;
total_minutes: number;
billed_minutes: number;
billable_minutes: number;
non_billable_minutes: number;
open_minutes: number;
billed_sessions: number;
billable_sessions: number;
non_billable_sessions: number;
open_sessions: number;
crm_billed_minutes: number;
crm_delta_minutes: number;
};
export type StatisticsOpenSession = {
id: string;
ticket_id: string;
ticket_number: string;
organization_name: string;
activity: string;
started_at: string;
rounded_minutes: number;
};
export type StatisticsCrmDay = {
ticket_id: string;
ticket_number: string;
organization_name: string;
day: string;
tracked_minutes: number;
crm_billed_minutes: number;
delta_minutes?: number;
acknowledged?: boolean;
acknowledged_at?: string | null;
};
export type StatisticsTeamspaceDay = {
ticket_id: string;
ticket_number: string;
organization_name: string;
day: string;
tracked_minutes: number;
crm_billed_minutes: number;
delta_minutes: number;
session_count: number;
project_teamspace_project: string | null;
};
export type StatisticsProjectFollowup = {
project_id: string;
ticket_id: string;
ticket_number: string;
title: string | null;
organization_name: string | null;
teamspace_project: string | null;
hour_budget_minutes: number | null;
consumption_minutes: number;
remaining_minutes: number | null;
latest_tt_update: string;
last_session_at: string | null;
project_updated_at: string;
open_followup_todo_id: string | null;
followup_state: "open" | "done" | "dismissed" | null;
open_todos: number;
overdue_todos: number;
};
export type StatisticsProjectFollowupOverview = {
enabled: boolean;
weeks: number;
secondEnabled: boolean;
secondWeeks: number;
createdTodos: number;
updatedTodos: number;
projects: StatisticsProjectFollowup[];
};
export type StatisticsOverview = {
periodType: "month" | "week";
period: string;
closed: boolean;
closedAt: string | null;
totals: {
tickets: number;
sessions: number;
minutes: number;
billedMinutes: number;
billableMinutes: number;
nonBillableMinutes: number;
openMinutes: number;
billedSessions: number;
billableSessions: number;
nonBillableSessions: number;
openSessions: number;
recurringMinutes: number;
manualMinutes: number;
recurringSessions: number;
manualSessions: number;
averageSessionMinutes: number;
activeDays: number;
crmBilledMinutes: number;
crmDeltaMinutes: number;
};
dailySeries: StatisticsDailyBucket[];
organizations: StatisticsGroup[];
workTypes: StatisticsGroup[];
tickets: StatisticsTicket[];
attention: {
openSessions: StatisticsOpenSession[];
missingCrmDays: StatisticsCrmDay[];
acknowledgedMissingCrmDays: StatisticsCrmDay[];
crmMismatches: StatisticsCrmDay[];
};
teamspaceDays: StatisticsTeamspaceDay[];
};