diff --git a/apps/calendar/src/app/[locale]/(root)/scheduler/components/AlgorithmInsights.tsx b/apps/calendar/src/app/[locale]/(root)/scheduler/components/AlgorithmInsights.tsx index 375b13e6e..219e953d2 100644 --- a/apps/calendar/src/app/[locale]/(root)/scheduler/components/AlgorithmInsights.tsx +++ b/apps/calendar/src/app/[locale]/(root)/scheduler/components/AlgorithmInsights.tsx @@ -10,8 +10,6 @@ import { CardHeader, CardTitle, } from '@tuturuuu/ui/card'; -import { Progress } from '@tuturuuu/ui/progress'; -import { Separator } from '@tuturuuu/ui/separator'; import { BrainIcon, CheckCircleIcon, @@ -21,7 +19,9 @@ import { TrendingUpIcon, XCircleIcon, ZapIcon, -} from 'lucide-react'; +} from '@tuturuuu/ui/icons'; +import { Progress } from '@tuturuuu/ui/progress'; +import { Separator } from '@tuturuuu/ui/separator'; import { useMemo } from 'react'; interface AlgorithmInsightsProps { diff --git a/apps/calendar/src/app/[locale]/(root)/scheduler/components/ScheduleDisplay.tsx b/apps/calendar/src/app/[locale]/(root)/scheduler/components/ScheduleDisplay.tsx index 738a0327a..b678398e2 100644 --- a/apps/calendar/src/app/[locale]/(root)/scheduler/components/ScheduleDisplay.tsx +++ b/apps/calendar/src/app/[locale]/(root)/scheduler/components/ScheduleDisplay.tsx @@ -9,16 +9,16 @@ import { CardHeader, CardTitle, } from '@tuturuuu/ui/card'; -import { Tooltip, TooltipContent, TooltipTrigger } from '@tuturuuu/ui/tooltip'; -import dayjs from 'dayjs'; -import relativeTime from 'dayjs/plugin/relativeTime'; import { AlertTriangleIcon, CalendarIcon, ClockIcon, SparklesIcon, TrendingUpIcon, -} from 'lucide-react'; +} from '@tuturuuu/ui/icons'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@tuturuuu/ui/tooltip'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; import { useMemo } from 'react'; dayjs.extend(relativeTime); diff --git a/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskList.tsx b/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskList.tsx index 5c9513b4e..4429ae772 100644 --- a/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskList.tsx +++ b/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskList.tsx @@ -10,6 +10,14 @@ import { CardHeader, CardTitle, } from '@tuturuuu/ui/card'; +import { + CalendarIcon, + CheckCircleIcon, + ClockIcon, + PlusIcon, + Trash2Icon, + ZapIcon, +} from '@tuturuuu/ui/icons'; import { Input } from '@tuturuuu/ui/input'; import { Label } from '@tuturuuu/ui/label'; import { Progress } from '@tuturuuu/ui/progress'; @@ -22,14 +30,6 @@ import { } from '@tuturuuu/ui/select'; import { Tooltip, TooltipContent, TooltipTrigger } from '@tuturuuu/ui/tooltip'; import dayjs from 'dayjs'; -import { - CalendarIcon, - CheckCircleIcon, - ClockIcon, - PlusIcon, - Trash2Icon, - ZapIcon, -} from 'lucide-react'; import { useMemo } from 'react'; interface TaskListProps { diff --git a/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskModal.tsx b/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskModal.tsx index d0066ff3a..2c1287040 100644 --- a/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskModal.tsx +++ b/apps/calendar/src/app/[locale]/(root)/scheduler/components/TaskModal.tsx @@ -10,6 +10,7 @@ import { DialogHeader, DialogTitle, } from '@tuturuuu/ui/dialog'; +import { CalendarIcon, ClockIcon, PlusIcon, TagIcon } from '@tuturuuu/ui/icons'; import { Input } from '@tuturuuu/ui/input'; import { Label } from '@tuturuuu/ui/label'; import { @@ -22,7 +23,6 @@ import { import { Separator } from '@tuturuuu/ui/separator'; import { Textarea } from '@tuturuuu/ui/textarea'; import dayjs from 'dayjs'; -import { CalendarIcon, ClockIcon, PlusIcon, TagIcon } from 'lucide-react'; import { useState } from 'react'; interface TaskModalProps { diff --git a/apps/calendar/src/app/[locale]/(root)/scheduler/components/TemplateScenarios.tsx b/apps/calendar/src/app/[locale]/(root)/scheduler/components/TemplateScenarios.tsx index 5f8975acb..84d3cf607 100644 --- a/apps/calendar/src/app/[locale]/(root)/scheduler/components/TemplateScenarios.tsx +++ b/apps/calendar/src/app/[locale]/(root)/scheduler/components/TemplateScenarios.tsx @@ -24,7 +24,7 @@ import { TrendingUpIcon, UsersIcon, ZapIcon, -} from 'lucide-react'; +} from '@tuturuuu/ui/icons'; interface TemplateScenarioProps { onLoadTemplate: (template: TemplateScenario) => void; diff --git a/apps/calendar/src/app/[locale]/(root)/scheduler/page.tsx b/apps/calendar/src/app/[locale]/(root)/scheduler/page.tsx index 508d2c2ec..0d9537dae 100644 --- a/apps/calendar/src/app/[locale]/(root)/scheduler/page.tsx +++ b/apps/calendar/src/app/[locale]/(root)/scheduler/page.tsx @@ -25,18 +25,18 @@ import { CardHeader, CardTitle, } from '@tuturuuu/ui/card'; -import { Input } from '@tuturuuu/ui/input'; -import { Label } from '@tuturuuu/ui/label'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@tuturuuu/ui/tabs'; -import { TooltipProvider } from '@tuturuuu/ui/tooltip'; -import dayjs from 'dayjs'; import { BrainIcon, CalendarIcon, ClockIcon, SettingsIcon, SparklesIcon, -} from 'lucide-react'; +} from '@tuturuuu/ui/icons'; +import { Input } from '@tuturuuu/ui/input'; +import { Label } from '@tuturuuu/ui/label'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@tuturuuu/ui/tabs'; +import { TooltipProvider } from '@tuturuuu/ui/tooltip'; +import dayjs from 'dayjs'; import { useState } from 'react'; function SchedulerPage() { diff --git a/apps/db/supabase/migrations/20250618070621_changed_check_creator_function_policy.sql b/apps/db/supabase/migrations/20250618070621_changed_check_creator_function_policy.sql new file mode 100644 index 000000000..83661119a --- /dev/null +++ b/apps/db/supabase/migrations/20250618070621_changed_check_creator_function_policy.sql @@ -0,0 +1,48 @@ +set check_function_bodies = off; + +CREATE OR REPLACE FUNCTION public.check_ws_creator(ws_id uuid) + RETURNS boolean + LANGUAGE plpgsql +AS $function$BEGIN + RETURN ( + ( + SELECT creator_id FROM public.workspaces WHERE id = check_ws_creator.ws_id + ) = auth.uid() + AND NOT EXISTS ( + SELECT 1 FROM public.workspace_subscription + WHERE public.workspace_subscription.ws_id = check_ws_creator.ws_id + ) + ); +END;$function$ +; + +drop policy "only allow owner of the user to buy subscription" on "public"."workspace_subscription"; + +create policy "only allow owner of the user to buy subscription" +on "public"."workspace_subscription" +as permissive +for insert +to authenticated +with check ((EXISTS ( SELECT 1 + FROM workspaces + WHERE ((workspaces.id = workspace_subscription.ws_id) AND (workspaces.creator_id = auth.uid()))))); + +drop policy "allow select for users that are in the workspace" on "public"."workspace_subscription"; + +create policy "allow select for users that are in the workspace" +on "public"."workspace_subscription" +as permissive +for select +to authenticated +using ((EXISTS ( SELECT 1 + FROM workspace_members wm + WHERE ((wm.user_id = auth.uid()) AND (wm.ws_id = workspace_subscription.ws_id))))); + +create policy "allow delete access for creator workspace" +on "public"."workspace_subscription" +as permissive +for delete +to authenticated +using ((EXISTS ( SELECT 1 + FROM workspaces + WHERE ((workspaces.id = workspace_subscription.ws_id) AND (workspaces.creator_id = auth.uid()))))); \ No newline at end of file diff --git a/apps/db/supabase/migrations/20250618073214_add_workspace_subscription_products_table.sql b/apps/db/supabase/migrations/20250618073214_add_workspace_subscription_products_table.sql new file mode 100644 index 000000000..57fa8c588 --- /dev/null +++ b/apps/db/supabase/migrations/20250618073214_add_workspace_subscription_products_table.sql @@ -0,0 +1,69 @@ +create table "public"."workspace_subscription_products" ( + "id" uuid not null, + "created_at" timestamp with time zone not null default now(), + "name" text, + "description" text, + "price" real, + "recurring_interval" text default 'month'::text +); + +alter table "public"."workspace_subscription" add column "product_id" uuid; + +CREATE UNIQUE INDEX workspace_subscription_products_pkey ON public.workspace_subscription_products USING btree (id); + +alter table "public"."workspace_subscription_products" add constraint "workspace_subscription_products_pkey" PRIMARY KEY using index "workspace_subscription_products_pkey"; + +alter table "public"."workspace_subscription" add constraint "workspace_subscription_product_id_fkey" FOREIGN KEY (product_id) REFERENCES workspace_subscription_products(id) not valid; + +alter table "public"."workspace_subscription" validate constraint "workspace_subscription_product_id_fkey"; + +grant delete on table "public"."workspace_subscription_products" to "anon"; + +grant insert on table "public"."workspace_subscription_products" to "anon"; + +grant references on table "public"."workspace_subscription_products" to "anon"; + +grant select on table "public"."workspace_subscription_products" to "anon"; + +grant trigger on table "public"."workspace_subscription_products" to "anon"; + +grant truncate on table "public"."workspace_subscription_products" to "anon"; + +grant update on table "public"."workspace_subscription_products" to "anon"; + +grant delete on table "public"."workspace_subscription_products" to "authenticated"; + +grant insert on table "public"."workspace_subscription_products" to "authenticated"; + +grant references on table "public"."workspace_subscription_products" to "authenticated"; + +grant select on table "public"."workspace_subscription_products" to "authenticated"; + +grant trigger on table "public"."workspace_subscription_products" to "authenticated"; + +grant truncate on table "public"."workspace_subscription_products" to "authenticated"; + +grant update on table "public"."workspace_subscription_products" to "authenticated"; + +grant delete on table "public"."workspace_subscription_products" to "service_role"; + +grant insert on table "public"."workspace_subscription_products" to "service_role"; + +grant references on table "public"."workspace_subscription_products" to "service_role"; + +grant select on table "public"."workspace_subscription_products" to "service_role"; + +grant trigger on table "public"."workspace_subscription_products" to "service_role"; + +grant truncate on table "public"."workspace_subscription_products" to "service_role"; + +grant update on table "public"."workspace_subscription_products" to "service_role"; + +alter table "public"."workspace_subscription_products" enable row level security; + +create policy "allow view for all products" +on "public"."workspace_subscription_products" +as permissive +for select +to authenticated +using (true); \ No newline at end of file diff --git a/apps/upskii/src/components/request-education-banner.tsx b/apps/upskii/src/components/request-education-banner.tsx index 7683f058f..453be2a28 100644 --- a/apps/upskii/src/components/request-education-banner.tsx +++ b/apps/upskii/src/components/request-education-banner.tsx @@ -1,8 +1,8 @@ 'use client'; import { RequestAccessButton } from './request-access-button'; +import { BookOpenText, Sparkles } from '@tuturuuu/ui/icons'; import clsx from 'clsx'; -import { BookOpenText, Sparkles } from 'lucide-react'; interface EducationBannerProps { workspaceName: string; diff --git a/apps/web/.env.example b/apps/web/.env.example index c33ec3fd8..236d47582 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -44,4 +44,4 @@ NEXT_PUBLIC_PROXY_API_KEY=YOUR_NEXT_PUBLIC_PROXY_API_KEY # Payment Polar.sh POLAR_WEBHOOK_SECRET=YOUR_POLAR_WEBHOOK_SECRET -NEXT_PUBLIC_POLAR_ACCESS_TOKEN=YOUR_NEXT_PUBLIC_POLAR_ACCESS_TOKEN \ No newline at end of file +NEXT_PUBLIC_POLAR_ACCESS_TOKEN=YOUR_NEXT_PUBLIC_POLAR_ACCESS_TOKEN diff --git a/apps/web/src/app/[locale]/(dashboard)/[wsId]/billing/billing-client.tsx b/apps/web/src/app/[locale]/(dashboard)/[wsId]/billing/billing-client.tsx index 76da8eea1..a3a2979e0 100644 --- a/apps/web/src/app/[locale]/(dashboard)/[wsId]/billing/billing-client.tsx +++ b/apps/web/src/app/[locale]/(dashboard)/[wsId]/billing/billing-client.tsx @@ -2,7 +2,7 @@ import PurchaseLink from './data-polar-checkout'; import { Button } from '@tuturuuu/ui/button'; -import { ArrowUpCircle, CheckCircle, CreditCard } from 'lucide-react'; +import { ArrowUpCircle, CheckCircle } from '@tuturuuu/ui/icons'; import { useState } from 'react'; // Define types for the props we're passing from the server component @@ -30,7 +30,9 @@ interface BillingClientProps { currentPlan: Plan; upgradePlans: UpgradePlan[]; wsId: string; + product_id: string; isCreator: boolean; + activeSubscriptionId?: string; } export function BillingClient({ @@ -38,8 +40,41 @@ export function BillingClient({ upgradePlans, wsId, isCreator, + // _product_id, + // activeSubscriptionId, }: BillingClientProps) { const [showUpgradeOptions, setShowUpgradeOptions] = useState(false); + const [_isLoading, _setIsLoading] = useState(false); + const [message, _setMessage] = useState(''); + + // const handleCancelSubscription = async () => { + // setIsLoading(true); + // setMessage(''); + + // const response = await fetch(`/api/${wsId}/${product_id}/cancel`, { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + + // body: JSON.stringify({ polarSubscriptionId: activeSubscriptionId }), + // }); + + // setIsLoading(false); + + // if (response.ok) { + // setMessage( + // 'Your subscription will be canceled at the end of your billing period.' + // ); + // // Reload the page to show the updated subscription status + // window.location.reload(); + // } else { + // const errorData = await response.json(); + // setMessage( + // `Error: ${errorData.error || 'Could not cancel subscription.'}` + // ); + // } + // }; return ( <> @@ -65,8 +100,8 @@ export function BillingClient({ -
Start Date
@@ -96,59 +131,54 @@ export function BillingClient({
- Visa ending in 4242 -
-Expires 05/2025
-+ No subscription history available. +
+ ) : ( ++ Subscription # + | ++ Date + | ++ Plan + | ++ Amount + | ++ Status + | ++ Actions + | +
---|---|---|---|---|---|
+ {subscription.id} + | ++ {getDisplayDate(subscription)} + | +
+
+
+
+ {subscription.product?.name || 'Unknown Plan'}
+
+ |
+
+ {subscription.product ? (
+
+
+ ) : (
+ 'N/A'
+ )}
+
+ ${subscription.product.price}
+
+
+ per {subscription.product.recurring_interval}
+
+ |
+ + + {subscription.status.charAt(0).toUpperCase() + + subscription.status.slice(1)} + {subscription.cancel_at_period_end && ' (Ending)'} + + | +
+
+ |
+
- Invoice # - | -- Date - | -- Amount - | -- Status - | -- Actions - | -
---|---|---|---|---|
- {payment.id} - | -- {payment.date} - | -- {payment.amount} - | -- - {payment.status} - - | -- - | -