Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"start": "wrangler dev",
"test": "vitest",
"cf-deploy": "wrangler deploy",
"cf-deploy:branch": "echo 'Per-branch deployments are not supported yet'",
"cf-types": "wrangler types",
"auth:generate": "pnpm dlx @better-auth/cli@latest generate --config ./better-auth.config.ts --output ../../packages/db/src/user.db.ts",
"sandbox:build": "cd ../.. && pnpm sandbox:build",
Expand Down
16 changes: 8 additions & 8 deletions apps/api/src/services/billing/Billing.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export class BillingService {

async getPlans(): Promise<
{
group: 'free' | 'starter' | 'pro' | 'scale';
group: 'free' | 'starter' | 'scale';
monthly: PlanEntity | null;
yearly: PlanEntity | null;
}[]
Expand All @@ -145,28 +145,28 @@ export class BillingService {

// below needs to be refactored, just easy way for frontend without custom plans
const newMap = new Map<
'free' | 'starter' | 'pro' | 'scale',
'free' | 'starter' | 'scale',
{
group: 'free' | 'starter' | 'pro' | 'scale';
group: 'free' | 'starter' | 'scale';
monthly: PlanEntity | null;
yearly: PlanEntity | null;
}
>();

allPlans.forEach((plan) => {
const cur = newMap.get(plan.group as 'free' | 'starter' | 'pro' | 'scale') || {
const cur = newMap.get(plan.group as 'free' | 'starter' | 'scale') || {
monthly: null,
yearly: null,
};
if (plan.interval === 'monthly') {
newMap.set(plan.group as 'free' | 'starter' | 'pro' | 'scale', {
group: plan.group as 'free' | 'starter' | 'pro' | 'scale',
newMap.set(plan.group as 'free' | 'starter' | 'scale', {
group: plan.group as 'free' | 'starter' | 'scale',
monthly: plan,
yearly: cur.yearly,
});
} else {
newMap.set(plan.group as 'free' | 'starter' | 'pro' | 'scale', {
group: plan.group as 'free' | 'starter' | 'pro' | 'scale',
newMap.set(plan.group as 'free' | 'starter' | 'scale', {
group: plan.group as 'free' | 'starter' | 'scale',
monthly: cur.monthly,
yearly: plan,
});
Expand Down
3 changes: 2 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"test": "vitest run",
"cf-types": "wrangler types",
"cf-dev": "wrangler dev",
"cf-deploy": "wrangler deploy"
"cf-deploy": "wrangler deploy",
"cf-deploy:branch": "echo 'Per-branch deployments are not supported yet'"
},
"dependencies": {
"@cloudflare/vite-plugin": "^1.7.5",
Expand Down
12 changes: 3 additions & 9 deletions apps/web/src/components/auth/RegisterView.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
RiArrowRightLine,
RiGithubFill,
RiGoogleFill,
RiMailCheckLine,
RiUserLine,
} from '@remixicon/react';
import { RiArrowRightLine, RiGithubFill, RiGoogleFill, RiMailCheckLine } from '@remixicon/react';
import { Link, useSearch } from '@tanstack/react-router';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -83,8 +77,8 @@ export const RegisterView = () => {
<div className='w-full max-w-md'>
<Card className='shadow-lg border-gray-200'>
<CardHeader className='text-center pb-4'>
<div className='w-12 h-12 bg-primary rounded-lg flex items-center justify-center mx-auto mb-4'>
<RiUserLine className='text-white size-6' />
<div className='mb-4 flex items-center justify-center'>
<img src='/logo.png' alt='logo' className='w-12 h-12 rounded-lg' />
</div>
<LargeHeader className='text-gray-900 mb-2'>Create account</LargeHeader>
<p className='text-gray-600'>Sign up to get started with your account</p>
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/components/auth/SignInView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RiArrowRightLine, RiGithubFill, RiGoogleFill, RiMailLine } from '@remixicon/react';
import { RiArrowRightLine, RiGithubFill, RiGoogleFill } from '@remixicon/react';
import { Link, useNavigate, useSearch } from '@tanstack/react-router';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -37,8 +37,8 @@ export const SignInView = () => {
<div className='w-full max-w-md'>
<Card className='shadow-lg border-gray-200 relative'>
<CardHeader className='text-center pb-4'>
<div className='w-12 h-12 bg-primary rounded-lg flex items-center justify-center mx-auto mb-4'>
<RiMailLine className='text-white size-6' />
<div className='mb-4 flex items-center justify-center'>
<img src='/logo.png' alt='logo' className='w-12 h-12 rounded-lg' />
</div>
<LargeHeader className='text-gray-900 mb-2'>Welcome back</LargeHeader>
<p className='text-gray-600'>Sign in to your account to continue</p>
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/components/room/whiteboard/WhiteboardView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { getBookmarkPreview } from './getBookmarkPreview';
import 'tldraw/tldraw.css';

// Configure the worker URL - this should match your API endpoint
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
const API_URL = import.meta.env.VITE_API_URL;
if (!API_URL) {
throw new Error('VITE_API_URL is not set');
}

export const WhiteboardView = () => {
const roomId = useCurrentRoomId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ interface UnfurlResponse {
title?: string;
}

const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
const API_URL = import.meta.env.VITE_API_URL;
if (!API_URL) {
throw new Error('VITE_API_URL is not set');
}

// How does our server handle bookmark unfurling?
export async function getBookmarkPreview({
Expand Down
48 changes: 24 additions & 24 deletions apps/web/src/components/settings/BillingPlanCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
RiHistoryLine,
RiLockPasswordLine,
RiPaletteLine,
RiStarLine,
RiSwap2Line,
RiTeamLine,
RiTerminalWindowFill,
Expand Down Expand Up @@ -45,34 +46,24 @@ const LIMIT_MAP = {
}
>;

// Feature mapping for each plan
const PLAN_FEATURE_MAP: Record<
'free' | 'starter' | 'pro' | 'scale' | 'enterprise',
'free' | 'starter' | 'scale' | 'enterprise',
Array<{
icon: RemixiconComponentType;
label: string;
subText?: string;
}>
> = {
free: [],
starter: [
free: [
{
icon: RiHistoryLine,
label: 'Interview Playback',
subText: `Replay interviews to review every step of a candidate's coding process`,
},
{
icon: RiGlobalLine,
label: 'Advanced Interviews',
subText: 'Multi-file & framework support. (React, Next.js, etc.)',
},
{
icon: RiBaseStationLine,
label: 'API Access',
subText: 'Access to our API for custom integrations',
},
{ icon: RiCustomerServiceLine, label: 'Basic Support' },
{ icon: RiCustomerServiceLine, label: 'Community Support' },
],
pro: [
starter: [
{
icon: RiHistoryLine,
label: 'Interview Playback',
Expand All @@ -88,7 +79,10 @@ const PLAN_FEATURE_MAP: Record<
label: 'API Access',
subText: 'Access to our API for custom integrations',
},
{ icon: RiCustomerServiceLine, label: 'Priority Support' },
{
icon: RiCustomerServiceLine,
label: 'Basic Support',
},
],
scale: [
{
Expand Down Expand Up @@ -149,7 +143,7 @@ const PLAN_FEATURE_MAP: Record<
},
{
icon: RiLockPasswordLine,
label: 'SSO Login',
label: 'Single Sign-On (SSO)',
},
{
icon: RiCustomerServiceLine,
Expand All @@ -169,7 +163,7 @@ export const BillingPlanCard = ({
isCreatingCheckout = false,
isLoading = false,
}: {
group: 'free' | 'starter' | 'pro' | 'scale' | 'enterprise';
group: 'free' | 'starter' | 'scale' | 'enterprise';
monthly: PlanSchema | null;
yearly: PlanSchema | null;
mode: PlanSchema['interval'];
Expand Down Expand Up @@ -213,7 +207,7 @@ export const BillingPlanCard = ({
const isCurrentPlan = currentPlanId === plan?.id;

// If no plan is available or is free plan, don't render anything
if (!plan || plan.price === 0) {
if (!plan) {
return null;
}

Expand All @@ -222,12 +216,15 @@ export const BillingPlanCard = ({
key={plan.id}
className={cx(
'relative transition-all duration-200 flex flex-col',
isPopular ? 'ring-2 ring-primary shadow-lg' : '',
isCurrentPlan ? 'ring-1 ring-green-300 bg-green-50/50 border-green-100' : ''
isPopular ? 'ring-2 ring-primary shadow-lg' : ''
// isCurrentPlan ? 'bg-green-500/10 border-green-500/20' : ''
)}
>
{isPopular && (
<Badge className='absolute top-4 right-4 bg-primary text-white'>Most Popular</Badge>
<Badge className='absolute top-4 right-4 bg-primary text-white'>
<RiStarLine className='w-3 h-3' />
Most Popular
</Badge>
)}

<CardHeader>
Expand All @@ -245,7 +242,7 @@ export const BillingPlanCard = ({
{plan.price > 0 && plan.name !== 'Enterprise' ? (
<>
<span className='text-sm font-normal text-muted-foreground ml-1'>
per {plan.interval}
per {plan.interval === 'monthly' ? 'month' : 'year'}
</span>
{isYearly && (
<Badge className='ml-2' variant='success'>
Expand All @@ -257,7 +254,10 @@ export const BillingPlanCard = ({
</div>

<Button
className='my-4'
className={cx(
'my-4',
isCurrentPlan ? 'disabled:text-primary/50 disabled:border-primary/50' : ''
)}
variant={isPopular ? 'primary' : 'secondary'}
icon={RiArrowRightLine}
iconPosition='right'
Expand Down
14 changes: 4 additions & 10 deletions apps/web/src/components/settings/BillingView.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { PlanSchema } from '@coderscreen/api/schema/billing';
import { RiSettings3Line } from '@remixicon/react';
import { useNavigate } from '@tanstack/react-router';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { useState } from 'react';
import { UsageCard } from '@/components/settings/UsageCard';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
Expand All @@ -22,16 +21,12 @@ import { BillingPlanCard } from './BillingPlanCard';
export const BillingView = () => {
const navigate = useNavigate();
const { customer } = useCustomer();
const { plans: allPlans, isLoading: isLoadingPlans } = usePlans();
const { plans, isLoading: isLoadingPlans } = usePlans();
const { createCheckoutSession, isLoading: isCreatingCheckout } = useCreateCheckoutSession();
const { createPortalSession, isLoading: isCreatingPortal } = useCreatePortalSession();
const { usage, isLoading } = useAllUsage();
const [billingMode, setBillingMode] = useState<'monthly' | 'yearly'>('yearly');

const plans = useMemo(() => {
return allPlans?.filter((plan) => plan.monthly?.price !== 0) ?? [];
}, [allPlans]);

// Get current plan from customer subscription
const currentPlan = customer?.plan;
const currentSubscription = customer?.subscription;
Expand Down Expand Up @@ -77,7 +72,6 @@ export const BillingView = () => {
window.location.href = portalSession.url;
} catch (error) {
console.error('Error creating portal session:', error);
toast.error('Failed to open billing portal');
}
};

Expand Down Expand Up @@ -162,7 +156,7 @@ export const BillingView = () => {
))
) : (
<>
{plans.map((plan, index) => (
{plans?.map((plan) => (
<BillingPlanCard
group={plan.group}
key={plan.monthly?.id || plan.yearly?.id}
Expand All @@ -172,7 +166,7 @@ export const BillingView = () => {
currentPlanId={currentPlan?.id}
onUpgrade={handleUpgrade}
isCreatingCheckout={isCreatingCheckout}
isPopular={index === Math.floor(plans.length / 2)}
isPopular={plan.group === 'scale'}
/>
))}

Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/contexts/RoomContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ interface RoomProviderProps {
}

const PARTY_NAME = 'room';
const API_URL = 'http://localhost:8000';
const API_URL = import.meta.env.VITE_API_URL;
if (!API_URL) {
throw new Error('VITE_API_URL is not set');
}

export const RoomProvider: React.FC<RoomProviderProps> = ({ children }) => {
const currentRoomId = useCurrentRoomId();
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/query/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import { hc } from 'hono/client';
import { toast } from 'sonner';
import { handleApiError } from '@/query/error.query';

const API_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:8000';
const API_URL = import.meta.env.VITE_API_URL;
if (!API_URL) {
throw new Error('VITE_API_URL is not set');
}

export const apiClient = hc<AppRouter>(API_URL, {
init: {
credentials: 'include',
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/query/realtime/notes.query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { useSession } from '@/query/auth.query';
import { getRandomColor } from '@/query/realtime/utils';

const PARTY_NAME = 'private-room';
const API_URL = 'http://localhost:8000';
const API_URL = import.meta.env.VITE_API_URL;
if (!API_URL) {
throw new Error('VITE_API_URL is not set');
}

// Shared hook for creating notes editor
export function useNotesEditor() {
Expand Down
6 changes: 0 additions & 6 deletions apps/web/src/query/room.query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { RoomSchema } from '@coderscreen/api/schema/room';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useRouter } from '@tanstack/react-router';
import { useCurrentRoomId } from '@/lib/params';
import { throwApiError } from '@/query/error.query';
import { apiClient } from './client';
Expand Down Expand Up @@ -163,7 +162,6 @@ export const useRoomCodeResult = () => {
};

export const useEndRoom = () => {
const router = useRouter();
const currentRoomId = useCurrentRoomId();
const queryClient = useQueryClient();
const mutation = useMutation({
Expand All @@ -178,10 +176,6 @@ export const useEndRoom = () => {
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['rooms', currentRoomId] });
router.navigate({
to: '/room/$roomId/summary',
params: { roomId: currentRoomId },
});
},
meta: {
SUCCESS_MESSAGE: 'Room ended successfully',
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"build:api": "npx nx build @coderscreen/api",
"deploy:web": "npx nx run @coderscreen/web:cf-deploy",
"deploy:api": "npx nx run @coderscreen/api:cf-deploy",
"deploy:branch:web": "npx nx run @coderscreen/web:cf-deploy:branch",
"deploy:branch:api": "npx nx run @coderscreen/api:cf-deploy:branch",
"sandbox:build": "docker build -t sandbox-image -f ./apps/api/src/containers/images/Dockerfile ."
},
"devDependencies": {
Expand Down