Add account hierarchy framework with migrations, RLS policies, and UI components

This commit is contained in:
T. Zehetbauer
2026-03-31 22:18:04 +02:00
parent 7e7da0b465
commit 59546ad6d2
262 changed files with 11671 additions and 3927 deletions

View File

@@ -1,6 +1,7 @@
import type { Database } from '@kit/supabase/database';
import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database } from '@kit/supabase/database';
import type {
CreateWaterInput,
UpdateWaterInput,
@@ -132,8 +133,10 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
const updateData: Record<string, unknown> = { updated_by: userId };
if (input.name !== undefined) updateData.name = input.name;
if (input.shortName !== undefined) updateData.short_name = input.shortName;
if (input.waterType !== undefined) updateData.water_type = input.waterType;
if (input.shortName !== undefined)
updateData.short_name = input.shortName;
if (input.waterType !== undefined)
updateData.water_type = input.waterType;
if (input.description !== undefined)
updateData.description = input.description;
if (input.surfaceAreaHa !== undefined)
@@ -189,37 +192,42 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
async getWaterDetail(waterId: string) {
// Fetch water + related data in parallel
const [waterResult, rulesResult, leasesResult, inspectorsResult, stockingResult] =
await Promise.all([
client.from('waters').select('*').eq('id', waterId).single(),
client
.from('water_species_rules')
.select(
'id, water_id, species_id, min_size_cm, protection_period_start, protection_period_end, max_catch_per_day, max_catch_per_year, created_at, fish_species ( id, name, name_latin )',
)
.eq('water_id', waterId),
client
.from('fishing_leases')
.select(
'id, lessor_name, start_date, end_date, initial_amount, fixed_annual_increase, percentage_annual_increase, payment_method, is_archived',
)
.eq('water_id', waterId)
.order('start_date', { ascending: false }),
client
.from('water_inspectors')
.select(
'id, water_id, member_id, assignment_start, assignment_end, created_at, members ( id, first_name, last_name )',
)
.eq('water_id', waterId),
client
.from('fish_stocking')
.select(
'id, stocking_date, quantity, weight_kg, age_class, cost_euros, remarks, fish_species ( id, name ), fish_suppliers ( id, name )',
)
.eq('water_id', waterId)
.order('stocking_date', { ascending: false })
.limit(20),
]);
const [
waterResult,
rulesResult,
leasesResult,
inspectorsResult,
stockingResult,
] = await Promise.all([
client.from('waters').select('*').eq('id', waterId).single(),
client
.from('water_species_rules')
.select(
'id, water_id, species_id, min_size_cm, protection_period_start, protection_period_end, max_catch_per_day, max_catch_per_year, created_at, fish_species ( id, name, name_latin )',
)
.eq('water_id', waterId),
client
.from('fishing_leases')
.select(
'id, lessor_name, start_date, end_date, initial_amount, fixed_annual_increase, percentage_annual_increase, payment_method, is_archived',
)
.eq('water_id', waterId)
.order('start_date', { ascending: false }),
client
.from('water_inspectors')
.select(
'id, water_id, member_id, assignment_start, assignment_end, created_at, members ( id, first_name, last_name )',
)
.eq('water_id', waterId),
client
.from('fish_stocking')
.select(
'id, stocking_date, quantity, weight_kg, age_class, cost_euros, remarks, fish_species ( id, name ), fish_suppliers ( id, name )',
)
.eq('water_id', waterId)
.order('stocking_date', { ascending: false })
.limit(20),
]);
if (waterResult.error) throw waterResult.error;
@@ -342,8 +350,7 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
if (input.spawningSeasonEnd !== undefined)
updateData.spawning_season_end = input.spawningSeasonEnd;
if (input.hasSpecialSpawningSeason !== undefined)
updateData.has_special_spawning_season =
input.hasSpecialSpawningSeason;
updateData.has_special_spawning_season = input.hasSpecialSpawningSeason;
if (input.kFactorAvg !== undefined)
updateData.k_factor_avg = input.kFactorAvg;
if (input.kFactorMin !== undefined)
@@ -741,8 +748,7 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
async updateCatchBook(input: UpdateCatchBookInput, userId: string) {
const updateData: Record<string, unknown> = { updated_by: userId };
if (input.memberId !== undefined)
updateData.member_id = input.memberId;
if (input.memberId !== undefined) updateData.member_id = input.memberId;
if (input.year !== undefined) updateData.year = input.year;
if (input.memberName !== undefined)
updateData.member_name = input.memberName;
@@ -782,7 +788,8 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
const { data, error } = await client
.from('catch_books')
.update({
status: 'eingereicht' as Database['public']['Enums']['catch_book_status'],
status:
'eingereicht' as Database['public']['Enums']['catch_book_status'],
is_submitted: true,
submitted_at: new Date().toISOString(),
updated_by: userId,
@@ -872,10 +879,7 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
return data;
},
async updateCatch(
catchId: string,
input: Partial<CreateCatchInput>,
) {
async updateCatch(catchId: string, input: Partial<CreateCatchInput>) {
const updateData: Record<string, unknown> = {};
if (input.speciesId !== undefined)
@@ -884,16 +888,14 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
if (input.catchDate !== undefined)
updateData.catch_date = input.catchDate;
if (input.quantity !== undefined) updateData.quantity = input.quantity;
if (input.lengthCm !== undefined)
updateData.length_cm = input.lengthCm;
if (input.lengthCm !== undefined) updateData.length_cm = input.lengthCm;
if (input.weightG !== undefined) updateData.weight_g = input.weightG;
if (input.sizeCategory !== undefined)
updateData.size_category = input.sizeCategory;
if (input.gender !== undefined) updateData.gender = input.gender;
if (input.isEmptyEntry !== undefined)
updateData.is_empty_entry = input.isEmptyEntry;
if (input.hasError !== undefined)
updateData.has_error = input.hasError;
if (input.hasError !== undefined) updateData.has_error = input.hasError;
if (input.remarks !== undefined) updateData.remarks = input.remarks;
const { data, error } = await client
@@ -907,10 +909,7 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
},
async deleteCatch(catchId: string) {
const { error } = await client
.from('catches')
.delete()
.eq('id', catchId);
const { error } = await client.from('catches').delete().eq('id', catchId);
if (error) throw error;
},
@@ -932,10 +931,7 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
// Permits
// =====================================================
async listPermits(
accountId: string,
opts?: { archived?: boolean },
) {
async listPermits(accountId: string, opts?: { archived?: boolean }) {
let query = client
.from('fishing_permits')
.select(
@@ -1121,8 +1117,7 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
if (input.competitionDate !== undefined)
updateData.competition_date = input.competitionDate;
if (input.eventId !== undefined) updateData.event_id = input.eventId;
if (input.permitId !== undefined)
updateData.permit_id = input.permitId;
if (input.permitId !== undefined) updateData.permit_id = input.permitId;
if (input.waterId !== undefined) updateData.water_id = input.waterId;
if (input.maxParticipants !== undefined)
updateData.max_participants = input.maxParticipants;
@@ -1363,8 +1358,7 @@ export function createFischereiApi(client: SupabaseClient<Database>) {
if (input.email !== undefined) updateData.email = input.email;
if (input.address !== undefined) updateData.address = input.address;
if (input.notes !== undefined) updateData.notes = input.notes;
if (input.isActive !== undefined)
updateData.is_active = input.isActive;
if (input.isActive !== undefined) updateData.is_active = input.isActive;
const { data, error } = await client
.from('fish_suppliers')