feat: Bottle Split System (Flaschenteilung)
- Add bottle_splits and split_participants tables with RLS - Implement soft-lock: pending requests count as reserved - Create /splits/create wizard (3 steps: bottle, pricing, shipping) - Create /splits/[slug] public page with price calculator - Create /splits/manage host dashboard with participant workflow - Add SplitProgressBar component for visual volume display - Status workflow: PENDING -> APPROVED -> PAID -> SHIPPED - Forum export (BBCode format) - Saved defaults in localStorage for glass/shipping costs
This commit is contained in:
@@ -445,3 +445,76 @@ DROP POLICY IF EXISTS "buddy_invites_redeem_policy" ON buddy_invites;
|
||||
CREATE POLICY "buddy_invites_redeem_policy" ON buddy_invites
|
||||
FOR SELECT USING (expires_at > now());
|
||||
|
||||
-- ============================================
|
||||
-- Bottle Splits (Flaschenteilung)
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bottle_splits (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
bottle_id UUID REFERENCES bottles(id) ON DELETE CASCADE UNIQUE,
|
||||
host_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
|
||||
total_volume INTEGER DEFAULT 70, -- in cl
|
||||
host_share INTEGER DEFAULT 10, -- what the host keeps, in cl
|
||||
price_bottle DECIMAL(10, 2) NOT NULL,
|
||||
cost_glass_5cl DECIMAL(10, 2) DEFAULT 0.80,
|
||||
cost_glass_10cl DECIMAL(10, 2) DEFAULT 1.50,
|
||||
shipping_options JSONB DEFAULT '[]'::jsonb,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
public_slug TEXT UNIQUE NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('Europe/Berlin'::text, now()),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('Europe/Berlin'::text, now())
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bottle_splits_host_id ON bottle_splits(host_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_bottle_splits_public_slug ON bottle_splits(public_slug);
|
||||
CREATE INDEX IF NOT EXISTS idx_bottle_splits_bottle_id ON bottle_splits(bottle_id);
|
||||
|
||||
ALTER TABLE bottle_splits ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Host can manage their own splits
|
||||
DROP POLICY IF EXISTS "bottle_splits_host_policy" ON bottle_splits;
|
||||
CREATE POLICY "bottle_splits_host_policy" ON bottle_splits
|
||||
FOR ALL USING ((SELECT auth.uid()) = host_id);
|
||||
|
||||
-- Anyone can view active splits (for public page)
|
||||
DROP POLICY IF EXISTS "bottle_splits_public_view" ON bottle_splits;
|
||||
CREATE POLICY "bottle_splits_public_view" ON bottle_splits
|
||||
FOR SELECT USING (is_active = true);
|
||||
|
||||
-- Split Participants
|
||||
CREATE TABLE IF NOT EXISTS split_participants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
split_id UUID REFERENCES bottle_splits(id) ON DELETE CASCADE NOT NULL,
|
||||
user_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
|
||||
amount_cl INTEGER NOT NULL CHECK (amount_cl IN (5, 10)),
|
||||
shipping_method TEXT NOT NULL,
|
||||
total_cost DECIMAL(10, 2) NOT NULL,
|
||||
status TEXT DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'APPROVED', 'PAID', 'SHIPPED', 'REJECTED', 'WAITLIST')),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('Europe/Berlin'::text, now()),
|
||||
UNIQUE(split_id, user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_split_participants_split_id ON split_participants(split_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_split_participants_user_id ON split_participants(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_split_participants_status ON split_participants(status);
|
||||
|
||||
ALTER TABLE split_participants ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Users can view their own participations
|
||||
DROP POLICY IF EXISTS "split_participants_own_policy" ON split_participants;
|
||||
CREATE POLICY "split_participants_own_policy" ON split_participants
|
||||
FOR ALL USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
-- Hosts can view/manage participants for their splits
|
||||
DROP POLICY IF EXISTS "split_participants_host_policy" ON split_participants;
|
||||
CREATE POLICY "split_participants_host_policy" ON split_participants
|
||||
FOR ALL USING (
|
||||
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- Anyone can view participants for public splits (to show fill-level)
|
||||
DROP POLICY IF EXISTS "split_participants_public_view" ON split_participants;
|
||||
CREATE POLICY "split_participants_public_view" ON split_participants
|
||||
FOR SELECT USING (
|
||||
split_id IN (SELECT id FROM bottle_splits WHERE is_active = true)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user