Skip to content

Commit 1661936

Browse files
authored
feat: subscription management (#67)
* Add subscription management feature spec - Requirements: 7 user stories with 31 acceptance criteria - Design: Database schema, composables, components, 23 correctness properties - Tasks: 16 top-level tasks with comprehensive property-based testing - Features: Multi-currency support, auto-renewal, lifecycle tracking - API integration: exchangerate-api.com with localStorage caching * feat: add database schema and types for subscription management - Add subscriptions table with RLS policies and constraints - Add user_preferences table for storing user settings - Add TypeScript interfaces for Subscription, Currency, ExchangeRate, UserPreference - Add display models and component prop/emit interfaces - Add indexes and triggers for both tables Implements task 1: Set up database schema and types Requirements: 1.1, 1.2, 1.3, 1.5, 2.1, 2.2, 2.3, 4.1, 4.2 * feat: implement useCurrency composable with exchange rate API integration - Add useCurrency composable with full currency conversion functionality - Implement fetchExchangeRates to call exchangerate-api.com - Add localStorage caching with 24-hour validity - Implement convert and getExchangeRate functions with inverse fallback - Add loadUserCurrencyPreference and setMainCurrency for database integration - Define supported currencies: CNY, USD, EUR, GBP, JPY, HKD - Add comprehensive unit tests (20 tests passing) - Validates Requirements 4.1, 4.2, 4.3, 4.4, 4.5 Task: 2.1 Create useCurrency composable with exchange rate API integration * feat: add property-based test for currency conversion accuracy - Install fast-check for property-based testing - Implement Property 10: Currency conversion accuracy test - Test validates that convert(amount, from, to) = amount × exchangeRate - Runs 100 iterations across all supported currency pairs - Validates Requirements 4.1, 4.3, 4.4 * feat: add Property 12 test for same currency display - Implement property-based test for same currency display - Validates that when original currency matches main currency, no conversion is applied - Tests that convert() returns original amount and getExchangeRate() returns 1 - Runs 100 iterations across all supported currencies - Validates Requirements 4.5 * feat: complete currency management composable with property tests - Implement useCurrency composable with exchange rate API integration - Add localStorage caching with 24-hour validity for exchange rates - Implement currency conversion functions (convert, getExchangeRate) - Add user preference management (loadUserCurrencyPreference, setMainCurrency) - Support 6 currencies: CNY, USD, EUR, GBP, JPY, HKD - Add Property 10: Currency conversion accuracy test (100 iterations) - Add Property 12: Same currency display test (100 iterations) - All 22 tests passing Validates Requirements: 4.1, 4.2, 4.3, 4.4, 4.5 Completes task 2 and subtasks 2.1, 2.2, 2.3 * feat: implement useSubscriptions composable with CRUD operations - Add loadSubscriptions function to fetch from database - Add createSubscription with comprehensive validation - Add updateSubscription with validation - Add deleteSubscription function - Add calculateNextBillingDate for monthly/yearly frequencies - Add snake_case to camelCase conversion utilities - Validate required fields, billing frequency, and auto-renew/end date relationship - Include proper error handling with Chinese error messages Task: 3.1 Create useSubscriptions composable with CRUD operations Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 2.1, 2.2, 2.3, 2.4, 2.5, 3.1, 3.3 * feat: add property test for subscription creation (Property 1) - Implement property-based test validating subscription creation with valid data - Test runs 100 iterations with randomly generated valid inputs - Validates Requirements 1.1 and 1.4 - Fix bug: convertKeysToCamelCase now properly converts null to undefined for endDate field - All 96 tests passing * feat: add property test for required field validation (Property 2) - Implements Property 2: Required field validation - Validates Requirements 1.2 - Tests that subscription creation rejects data with missing required fields - Verifies appropriate error messages are thrown for validation failures - Runs 100 iterations with various combinations of missing fields - All tests passing * feat: add property test for billing frequency validation (Property 3) - Implements Property 3: Billing frequency validation - Validates Requirements 1.3 - Tests that only 'monthly' or 'yearly' are accepted as valid billing frequencies - Generates 100 test cases with invalid billing frequency values - Verifies proper error handling and Chinese error messages - All tests passing * feat: add property test for end date and auto-renew relationship - Implement Property 4 test validating Requirements 1.5 - Test verifies invalid combinations are rejected (auto-renew with end date, non-auto-renew without end date) - Test verifies valid combinations are accepted - Runs 100 iterations with randomly generated subscription data - All tests passing * feat: add property test for subscription update persistence (Property 5) * feat: add property test for auto-renew to fixed end date transition - Implement Property 6 test validating Requirements 2.2 - Test ensures end date is required when changing from auto-renew to fixed end date - Validates both rejection (no end date) and acceptance (with end date) cases - Runs 100 iterations with randomly generated subscription data - All tests passing * feat: add Property 7 test for fixed end date to auto-renew transition - Implements property-based test validating Requirements 2.3 - Tests that changing subscription from fixed end date to auto-renew clears the end date - Verifies end date is set to undefined when transitioning to auto-renew - Runs 100 iterations with random subscription data - All tests passing * feat: add property test for currency change recalculation (Property 8) - Implements Property 8: Currency change recalculation test - Validates Requirement 2.5: currency modification and display amount recalculation - Tests that subscription currency updates are properly persisted - Runs 100 iterations across different currency combinations - All tests passing * feat: add property test for subscription deletion (Property 9) - Implement Property 9: Subscription deletion test - Validates Requirements 3.1, 3.3 - Tests that deleted subscriptions are removed from both database and list - Verifies other subscriptions remain unaffected - Runs 100 iterations with random subscription data - All tests passing * Complete task 3: subscription management composable with all property tests passing * feat: implement useSubscriptionCalculations composable - Add calculateMonthlyEquivalent and calculateYearlyEquivalent functions - Add isExpired, isEndingSoon, and getDaysUntilEnd lifecycle checks - Add calculateTotalCosts for aggregate subscription calculations - Excludes expired subscriptions from totals - Supports 30-day threshold for ending soon detection Implements task 4.1 from subscription-management spec Requirements: 6.3, 6.4, 6.5, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6 * feat: add property tests for subscription calculations composable - Implement Property 19: Expiration detection tests (3 variants) - Test expired subscriptions (past end dates) - Test auto-renew subscriptions never expire - Test future end dates are not expired - Implement Property 18: Ending soon detection tests (4 variants) - Test subscriptions ending within 30 days - Test auto-renew subscriptions never ending soon - Test far future end dates not ending soon - Test expired subscriptions not ending soon - Implement Property 21: Monthly total calculation test - Validates sum of monthly + yearly/12 subscriptions - Implement Property 22: Yearly total calculation test - Validates sum of yearly + monthly*12 subscriptions - Implement Property 23: Expired subscription exclusion test - Validates expired subscriptions excluded from totals - Validates active subscriptions included in totals All tests run 100 iterations and validate requirements 6.3, 6.4, 6.5, 7.1-7.6 * feat: implement SubscriptionForm component - Create SubscriptionForm.vue with all required fields - Add form validation for required fields and positive amounts - Implement conditional end date field based on renewal type - Add currency selector with supported currencies - Add billing frequency and renewal type radio buttons - Handle both create and edit modes - Emit save and update events - Validate future dates for end date field - Calculate next billing date automatically Implements task 5.1 from subscription-management spec Validates requirements: 1.1, 1.2, 1.3, 1.5, 2.1, 2.2, 2.3, 2.5 * feat: implement SubscriptionCard component - Add SubscriptionCard.vue component with full display features - Display subscription name, amount, and billing frequency - Show original currency when different from main currency - Display auto-renew indicator or end date - Show warning for subscriptions ending soon - Display expired badge for past end dates - Show next billing date for active subscriptions - Add edit and delete action buttons with proper emits - Follow existing Vuetify styling patterns - Include Chinese labels consistent with app - Add ARIA labels for accessibility Implements task 6.1 from subscription-management spec Validates requirements: 4.2, 4.5, 5.3, 6.1, 6.2, 6.3, 6.4, 6.5 * feat: add property-based tests for SubscriptionCard display - Implement Property 11: Dual currency display validation - Implement Property 14: Subscription display information validation - Implement Property 16: Auto-renew indicator validation - Implement Property 17: End date display validation - Implement Property 20: Next billing date calculation validation All tests use fast-check with 100 iterations and verify correctness properties for subscription display data structures. Validates Requirements: 4.2, 5.3, 6.1, 6.2, 6.5 * feat: implement SubscriptionList component - Add SubscriptionList.vue component with active/expired grouping - Display loading state with spinner and Chinese text - Show empty state when no subscriptions exist - Group subscriptions by status (active/expired) with counts - Forward edit and delete events from SubscriptionCard - Follow existing codebase patterns and Chinese localization - Mark task 7.1 as completed Validates Requirements 5.1, 5.3 * feat: add property test for subscription list completeness - Implement Property 13: Subscription list completeness - Validates Requirements 5.1 - Tests that all subscriptions are displayed when navigating to subscriptions tab - Verifies grouping logic maintains completeness (no lost/duplicated subscriptions) - Runs 100 iterations with random subscription data - Test passes successfully * feat: implement SubscriptionSummary component - Add SubscriptionSummary.vue component with card layout - Display total monthly and yearly costs - Show active subscription count - Display currency indicator with localized names - Include currency formatting utilities - Responsive grid layout for mobile and desktop - Mark task 8.1 as completed in tasks.md Requirements: 7.1, 7.2 * feat: implement CurrencySettings component for subscription management - Add CurrencySettings.vue component with currency selector - Display current main currency with symbol, code, and name - Provide dropdown with all supported currencies (CNY, USD, EUR, GBP, JPY, HKD) - Emit update event when currency changes - Include help text explaining currency conversion - Follow existing Vuetify card-based design pattern - Implement proper TypeScript types and v-model pattern - Complete task 9.1 from subscription management spec Requirements: 4.1, 4.3 * Add SubscriptionsView component and update tasks * feat: implement SubscriptionsView with state persistence - Implement main SubscriptionsView component with full CRUD operations - Add currency settings, subscription summary, and list display - Implement delete confirmation dialog - Add comprehensive error handling and loading states - Fix useSubscriptions composable to use module-level refs for state persistence - Add Property 15 test validating state persistence across navigation - All tests passing (10/10) Validates Requirements: 1.1, 1.4, 2.1, 2.4, 3.1, 3.3, 4.1, 4.3, 5.1, 5.5 * feat: add subscriptions tab to bottom navigation - Add subscriptions button with mdi-sync icon - Add Chinese label '订阅' (subscriptions) - Update route mapping to handle Subscriptions route - Implements task 11.1 (Requirements 5.2, 5.4) * feat: add subscriptions route to router - Add /subscriptions route with requiresAuth meta flag - Import SubscriptionsView component in router - Complete task 11: Add subscriptions tab to navigation - Validates Requirements 5.1, 5.2 * Mark task 12 (database migration scripts) as complete - schema already in db/schema.sql * feat: add confirmation dialog for subscription deletion - Implement Vuetify confirmation dialog in SubscriptionsView - Display subscription name in confirmation message - Add cancel and confirm actions with loading state - Include proper error handling and user feedback - Validates Requirements 3.2 Task 14.1 complete * feat: add comprehensive error handling for subscriptions * fix: make schema idempotent with IF NOT EXISTS and DROP IF EXISTS * docs: document PGRST116 error as expected behavior in loadUserCurrencyPreference * feat: move subscriptions tab to middle position in bottom navigation * feat: improve subscriptions UI - move currency settings to user menu, center FAB, optimize layout * feat: improve currency settings UX - add save button, fullscreen dialog, loading state, and fix HKD symbol consistency * feat: standardize dialog UI - consistent button styles and fullscreen support * feat: add validation to password change button - disable when fields empty or passwords don't match * style: reduce subscription summary text size from h5 to h6 for consistency * fix: move FAB to SubscriptionsView to fix add button functionality * feat: add start_date to subscriptions and improve UI layout - Add start_date field to subscriptions table and TypeScript types - Update SubscriptionForm to include start date input - Calculate next billing date based on start date - Improve SubscriptionCard layout: - Display badges (billing frequency, auto-renew, expiring soon) inline - Hide next billing date when end date is before next billing - Show '下次续订' for auto-renew vs '下次扣费' for fixed end - Reduce spacing between edit/delete icons - Update ExpenseList to match icon spacing - Update empty state text to reflect centered FAB position * fix: use timezone-aware date handling in subscription form - Import and use getTodayDate() and formatDateToLocal() from dateUtils - Ensures dates are handled in local timezone, preventing day shifts - Matches the date handling approach used in expense form * feat: add detailed breakdown dialogs for subscription costs - Add info icons next to monthly and yearly total costs - Show breakdown of each active subscription's contribution - Display calculation method (e.g., '年费 ÷ 12' or '月费 × 12') - Include billing frequency badges for each subscription - Show total at bottom of breakdown
1 parent c9c36ad commit 1661936

27 files changed

Lines changed: 5406 additions & 27 deletions

db/schema.sql

Lines changed: 130 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
-- System-wide category templates (admin managed)
2-
CREATE TABLE system_categories (
2+
CREATE TABLE IF NOT EXISTS system_categories (
33
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
44
parent_id UUID REFERENCES system_categories(id) ON DELETE CASCADE,
55
name TEXT NOT NULL,
@@ -21,7 +21,7 @@ CREATE TABLE system_categories (
2121
);
2222

2323
-- Categories table for dynamic category management
24-
CREATE TABLE categories (
24+
CREATE TABLE IF NOT EXISTS categories (
2525
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
2626
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
2727
parent_id UUID REFERENCES categories(id) ON DELETE CASCADE,
@@ -45,7 +45,7 @@ CREATE TABLE categories (
4545
);
4646

4747
-- Create expenses table
48-
CREATE TABLE expenses (
48+
CREATE TABLE IF NOT EXISTS expenses (
4949
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
5050
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
5151
amount DECIMAL(10,2) NOT NULL,
@@ -57,64 +57,73 @@ CREATE TABLE expenses (
5757
);
5858

5959
-- Create indexes for better performance
60-
CREATE INDEX system_categories_parent_id_idx ON system_categories(parent_id);
61-
CREATE INDEX system_categories_set_locale_idx ON system_categories(category_set, locale);
62-
CREATE INDEX categories_user_id_idx ON categories(user_id);
63-
CREATE INDEX categories_parent_id_idx ON categories(parent_id);
64-
CREATE INDEX categories_system_id_idx ON categories(system_category_id);
65-
CREATE INDEX categories_path_idx ON categories(path);
66-
CREATE INDEX categories_sort_order_idx ON categories(user_id, parent_id, sort_order);
60+
CREATE INDEX IF NOT EXISTS system_categories_parent_id_idx ON system_categories(parent_id);
61+
CREATE INDEX IF NOT EXISTS system_categories_set_locale_idx ON system_categories(category_set, locale);
62+
CREATE INDEX IF NOT EXISTS categories_user_id_idx ON categories(user_id);
63+
CREATE INDEX IF NOT EXISTS categories_parent_id_idx ON categories(parent_id);
64+
CREATE INDEX IF NOT EXISTS categories_system_id_idx ON categories(system_category_id);
65+
CREATE INDEX IF NOT EXISTS categories_path_idx ON categories(path);
66+
CREATE INDEX IF NOT EXISTS categories_sort_order_idx ON categories(user_id, parent_id, sort_order);
6767

6868
-- Create index on user_id for better performance
69-
CREATE INDEX expenses_user_id_idx ON expenses(user_id);
69+
CREATE INDEX IF NOT EXISTS expenses_user_id_idx ON expenses(user_id);
7070

7171
-- Create index on category_id for better performance
72-
CREATE INDEX expenses_category_id_idx ON expenses(category_id);
72+
CREATE INDEX IF NOT EXISTS expenses_category_id_idx ON expenses(category_id);
7373

7474
-- Create index on date and updated_at for better sorting performance
75-
CREATE INDEX expenses_date_updated_at_idx ON expenses(date DESC, updated_at DESC);
75+
CREATE INDEX IF NOT EXISTS expenses_date_updated_at_idx ON expenses(date DESC, updated_at DESC);
7676

7777
-- Create index on date for better performance
78-
CREATE INDEX expenses_date_idx ON expenses(date);
78+
CREATE INDEX IF NOT EXISTS expenses_date_idx ON expenses(date);
7979

8080
-- Create index on created_at for pagination performance
81-
CREATE INDEX expenses_created_at_idx ON expenses(created_at);
81+
CREATE INDEX IF NOT EXISTS expenses_created_at_idx ON expenses(created_at);
8282

8383
-- Enable Row Level Security (RLS)
8484
ALTER TABLE expenses ENABLE ROW LEVEL SECURITY;
8585
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
8686
ALTER TABLE system_categories ENABLE ROW LEVEL SECURITY;
8787

8888
-- RLS Policies for system_categories (Admin-only access)
89+
DROP POLICY IF EXISTS "System categories are read-only for authenticated users" ON system_categories;
8990
CREATE POLICY "System categories are read-only for authenticated users" ON system_categories
9091
FOR SELECT USING (auth.role() = 'authenticated');
9192

9293
-- No INSERT, UPDATE, or DELETE policies for system_categories
9394
-- This means only service_role (Supabase console/admin) can modify them
9495

9596
-- RLS Policies for categories
97+
DROP POLICY IF EXISTS "Users can view own categories" ON categories;
9698
CREATE POLICY "Users can view own categories" ON categories
9799
FOR SELECT USING (auth.uid() = user_id);
98100

101+
DROP POLICY IF EXISTS "Users can insert own categories" ON categories;
99102
CREATE POLICY "Users can insert own categories" ON categories
100103
FOR INSERT WITH CHECK (auth.uid() = user_id);
101104

105+
DROP POLICY IF EXISTS "Users can update own categories" ON categories;
102106
CREATE POLICY "Users can update own categories" ON categories
103107
FOR UPDATE USING (auth.uid() = user_id);
104108

109+
DROP POLICY IF EXISTS "Users can delete own categories" ON categories;
105110
CREATE POLICY "Users can delete own categories" ON categories
106111
FOR DELETE USING (auth.uid() = user_id AND is_default = FALSE);
107112

108113
-- RLS Policies for expenses
114+
DROP POLICY IF EXISTS "Users can view own expenses" ON expenses;
109115
CREATE POLICY "Users can view own expenses" ON expenses
110116
FOR SELECT USING (auth.uid() = user_id);
111117

118+
DROP POLICY IF EXISTS "Users can insert own expenses" ON expenses;
112119
CREATE POLICY "Users can insert own expenses" ON expenses
113120
FOR INSERT WITH CHECK (auth.uid() = user_id);
114121

122+
DROP POLICY IF EXISTS "Users can update own expenses" ON expenses;
115123
CREATE POLICY "Users can update own expenses" ON expenses
116124
FOR UPDATE USING (auth.uid() = user_id);
117125

126+
DROP POLICY IF EXISTS "Users can delete own expenses" ON expenses;
118127
CREATE POLICY "Users can delete own expenses" ON expenses
119128
FOR DELETE USING (auth.uid() = user_id);
120129

@@ -198,22 +207,127 @@ END;
198207
$$ LANGUAGE plpgsql;
199208

200209
-- Triggers
210+
DROP TRIGGER IF EXISTS update_system_categories_updated_at ON system_categories;
201211
CREATE TRIGGER update_system_categories_updated_at
202212
BEFORE UPDATE ON system_categories
203213
FOR EACH ROW
204214
EXECUTE FUNCTION update_updated_at_column();
205215

216+
DROP TRIGGER IF EXISTS update_system_categories_hierarchy ON system_categories;
206217
CREATE TRIGGER update_system_categories_hierarchy
207218
BEFORE INSERT OR UPDATE ON system_categories
208219
FOR EACH ROW
209220
EXECUTE FUNCTION update_system_category_hierarchy();
210221

222+
DROP TRIGGER IF EXISTS update_categories_updated_at ON categories;
211223
CREATE TRIGGER update_categories_updated_at
212224
BEFORE UPDATE ON categories
213225
FOR EACH ROW
214226
EXECUTE FUNCTION update_updated_at_column();
215227

228+
DROP TRIGGER IF EXISTS update_categories_hierarchy ON categories;
216229
CREATE TRIGGER update_categories_hierarchy
217230
BEFORE INSERT OR UPDATE ON categories
218231
FOR EACH ROW
219-
EXECUTE FUNCTION update_category_hierarchy();
232+
EXECUTE FUNCTION update_category_hierarchy();
233+
234+
-- Subscriptions table for tracking recurring subscriptions
235+
CREATE TABLE IF NOT EXISTS subscriptions (
236+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
237+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
238+
name TEXT NOT NULL,
239+
amount DECIMAL(10,2) NOT NULL CHECK (amount > 0),
240+
currency TEXT NOT NULL,
241+
billing_frequency TEXT NOT NULL CHECK (billing_frequency IN ('monthly', 'yearly')),
242+
is_auto_renew BOOLEAN NOT NULL DEFAULT TRUE,
243+
start_date DATE NOT NULL DEFAULT CURRENT_DATE,
244+
end_date DATE,
245+
next_billing_date DATE NOT NULL,
246+
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
247+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
248+
249+
-- Ensure auto-renew and end_date relationship is valid
250+
CHECK (
251+
(is_auto_renew = TRUE AND end_date IS NULL) OR
252+
(is_auto_renew = FALSE AND end_date IS NOT NULL)
253+
),
254+
-- Ensure end_date is after start_date
255+
CHECK (end_date IS NULL OR end_date >= start_date)
256+
);
257+
258+
-- User preferences table for storing user-specific settings
259+
CREATE TABLE IF NOT EXISTS user_preferences (
260+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
261+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
262+
category TEXT NOT NULL,
263+
key TEXT NOT NULL,
264+
value TEXT NOT NULL,
265+
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
266+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL,
267+
268+
-- Ensure unique preference per user, category, and key
269+
UNIQUE(user_id, category, key)
270+
);
271+
272+
-- Create indexes for subscriptions
273+
CREATE INDEX IF NOT EXISTS subscriptions_user_id_idx ON subscriptions(user_id);
274+
CREATE INDEX IF NOT EXISTS subscriptions_start_date_idx ON subscriptions(start_date);
275+
CREATE INDEX IF NOT EXISTS subscriptions_next_billing_date_idx ON subscriptions(next_billing_date);
276+
CREATE INDEX IF NOT EXISTS subscriptions_end_date_idx ON subscriptions(end_date);
277+
278+
-- Create indexes for user_preferences
279+
CREATE INDEX IF NOT EXISTS user_preferences_user_id_idx ON user_preferences(user_id);
280+
CREATE INDEX IF NOT EXISTS user_preferences_category_idx ON user_preferences(user_id, category);
281+
282+
-- Enable Row Level Security for subscriptions
283+
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
284+
285+
-- RLS Policies for subscriptions
286+
DROP POLICY IF EXISTS "Users can view own subscriptions" ON subscriptions;
287+
CREATE POLICY "Users can view own subscriptions" ON subscriptions
288+
FOR SELECT USING (auth.uid() = user_id);
289+
290+
DROP POLICY IF EXISTS "Users can insert own subscriptions" ON subscriptions;
291+
CREATE POLICY "Users can insert own subscriptions" ON subscriptions
292+
FOR INSERT WITH CHECK (auth.uid() = user_id);
293+
294+
DROP POLICY IF EXISTS "Users can update own subscriptions" ON subscriptions;
295+
CREATE POLICY "Users can update own subscriptions" ON subscriptions
296+
FOR UPDATE USING (auth.uid() = user_id);
297+
298+
DROP POLICY IF EXISTS "Users can delete own subscriptions" ON subscriptions;
299+
CREATE POLICY "Users can delete own subscriptions" ON subscriptions
300+
FOR DELETE USING (auth.uid() = user_id);
301+
302+
-- Enable Row Level Security for user_preferences
303+
ALTER TABLE user_preferences ENABLE ROW LEVEL SECURITY;
304+
305+
-- RLS Policies for user_preferences
306+
DROP POLICY IF EXISTS "Users can view own preferences" ON user_preferences;
307+
CREATE POLICY "Users can view own preferences" ON user_preferences
308+
FOR SELECT USING (auth.uid() = user_id);
309+
310+
DROP POLICY IF EXISTS "Users can insert own preferences" ON user_preferences;
311+
CREATE POLICY "Users can insert own preferences" ON user_preferences
312+
FOR INSERT WITH CHECK (auth.uid() = user_id);
313+
314+
DROP POLICY IF EXISTS "Users can update own preferences" ON user_preferences;
315+
CREATE POLICY "Users can update own preferences" ON user_preferences
316+
FOR UPDATE USING (auth.uid() = user_id);
317+
318+
DROP POLICY IF EXISTS "Users can delete own preferences" ON user_preferences;
319+
CREATE POLICY "Users can delete own preferences" ON user_preferences
320+
FOR DELETE USING (auth.uid() = user_id);
321+
322+
-- Triggers for subscriptions and user_preferences
323+
DROP TRIGGER IF EXISTS update_subscriptions_updated_at ON subscriptions;
324+
CREATE TRIGGER update_subscriptions_updated_at
325+
BEFORE UPDATE ON subscriptions
326+
FOR EACH ROW
327+
EXECUTE FUNCTION update_updated_at_column();
328+
329+
DROP TRIGGER IF EXISTS update_user_preferences_updated_at ON user_preferences;
330+
CREATE TRIGGER update_user_preferences_updated_at
331+
BEFORE UPDATE ON user_preferences
332+
FOR EACH ROW
333+
EXECUTE FUNCTION update_updated_at_column();

package-lock.json

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@tailwindcss/postcss": "^4.1.8",
3434
"@vitest/ui": "^3.2.4",
3535
"eslint": "^9.28.0",
36+
"fast-check": "^4.3.0",
3637
"globals": "^16.0.0",
3738
"happy-dom": "^20.0.0",
3839
"tailwindcss": "^4.0.17",

0 commit comments

Comments
 (0)