feat: enhance member management features; add quick stats and search capabilities
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
-- Migration: Enhanced member search and quick stats
|
||||
-- Adds: full-text search index, quick stats RPC, next member number function
|
||||
|
||||
-- Full-text search index (German) for faster member search
|
||||
CREATE INDEX IF NOT EXISTS ix_members_fulltext ON public.members
|
||||
USING gin(
|
||||
to_tsvector(
|
||||
'german',
|
||||
coalesce(first_name, '') || ' ' ||
|
||||
coalesce(last_name, '') || ' ' ||
|
||||
coalesce(email, '') || ' ' ||
|
||||
coalesce(member_number, '') || ' ' ||
|
||||
coalesce(city, '')
|
||||
)
|
||||
);
|
||||
|
||||
-- Trigram index on names for fuzzy / ILIKE search
|
||||
CREATE INDEX IF NOT EXISTS ix_members_name_trgm
|
||||
ON public.members
|
||||
USING gin ((lower(first_name || ' ' || last_name)) gin_trgm_ops);
|
||||
|
||||
-- Quick stats RPC — returns a single row with KPI counts
|
||||
-- Includes has_role_on_account guard to prevent cross-tenant data leaks
|
||||
CREATE OR REPLACE FUNCTION public.get_member_quick_stats(p_account_id uuid)
|
||||
RETURNS TABLE(
|
||||
total bigint,
|
||||
active bigint,
|
||||
inactive bigint,
|
||||
pending bigint,
|
||||
resigned bigint,
|
||||
new_this_year bigint,
|
||||
pending_applications bigint
|
||||
)
|
||||
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
||||
SET search_path = ''
|
||||
AS $$
|
||||
BEGIN
|
||||
-- Verify caller has access to this account
|
||||
IF NOT public.has_role_on_account(p_account_id) THEN
|
||||
RAISE EXCEPTION 'Access denied to account %', p_account_id;
|
||||
END IF;
|
||||
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
count(*)::bigint AS total,
|
||||
count(*) FILTER (WHERE m.status = 'active')::bigint AS active,
|
||||
count(*) FILTER (WHERE m.status = 'inactive')::bigint AS inactive,
|
||||
count(*) FILTER (WHERE m.status = 'pending')::bigint AS pending,
|
||||
count(*) FILTER (WHERE m.status = 'resigned')::bigint AS resigned,
|
||||
count(*) FILTER (WHERE m.status = 'active'
|
||||
AND m.entry_date >= date_trunc('year', current_date)::date)::bigint AS new_this_year,
|
||||
(
|
||||
SELECT count(*)
|
||||
FROM public.membership_applications a
|
||||
WHERE a.account_id = p_account_id
|
||||
AND a.status = 'submitted'
|
||||
)::bigint AS pending_applications
|
||||
FROM public.members m
|
||||
WHERE m.account_id = p_account_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.get_member_quick_stats(uuid) TO authenticated;
|
||||
|
||||
-- Next member number: returns max(member_number) + 1 as text
|
||||
-- Includes has_role_on_account guard
|
||||
CREATE OR REPLACE FUNCTION public.get_next_member_number(p_account_id uuid)
|
||||
RETURNS text
|
||||
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
||||
SET search_path = ''
|
||||
AS $$
|
||||
DECLARE
|
||||
v_result text;
|
||||
BEGIN
|
||||
-- Verify caller has access to this account
|
||||
IF NOT public.has_role_on_account(p_account_id) THEN
|
||||
RAISE EXCEPTION 'Access denied to account %', p_account_id;
|
||||
END IF;
|
||||
|
||||
SELECT LPAD(
|
||||
(COALESCE(
|
||||
MAX(
|
||||
CASE
|
||||
WHEN member_number ~ '^\d+$' THEN member_number::integer
|
||||
ELSE 0
|
||||
END
|
||||
),
|
||||
0
|
||||
) + 1)::text,
|
||||
4,
|
||||
'0'
|
||||
) INTO v_result
|
||||
FROM public.members
|
||||
WHERE account_id = p_account_id;
|
||||
|
||||
RETURN v_result;
|
||||
END;
|
||||
$$;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.get_next_member_number(uuid) TO authenticated;
|
||||
Reference in New Issue
Block a user