diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index fef9e5c..2329c93 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -39,10 +39,16 @@ pnpm build # Build only apps (main, external) pnpm build:apps + +# Build main app for specific environments +pnpm build:staging # Build for staging +pnpm build:prod # Build for production ``` **Note:** The `@xtablo/shared` and `@xtablo/ui` packages are source-only packages. They export TypeScript source files directly and are consumed by app bundlers (Vite) without a separate build step. This is faster and simpler for development. +**Environment Builds:** The main app supports environment-specific builds (`staging`, `production`) that are properly cached by Turborepo based on environment-specific inputs (`.env.staging`, `.env.production`, etc.). + ### Development ```bash @@ -138,6 +144,22 @@ turbo build --filter='./packages/*' turbo build --filter='!@xtablo/external' ``` +### Package-Level Configuration + +Packages can have their own `turbo.json` file to define custom tasks or override root configuration: + +**Example:** `apps/main/turbo.json` defines environment-specific builds: + +- `build:staging` - Builds for staging with `.env.staging` +- `build:prod` - Builds for production with `.env.production` + +Each task: + +- Extends the root config with `"extends": ["//"]` +- Defines specific inputs (including environment files) +- Configures caching with appropriate outputs +- Can pass environment variables to the build process + ## Package Development Workflow ### 1. Working on Packages @@ -244,6 +266,8 @@ pnpm build ## CI/CD Considerations +### Basic Pipeline + For CI/CD pipelines: ```bash @@ -259,6 +283,45 @@ pnpm typecheck pnpm test ``` +### Environment-Specific Deployments + +**Staging Pipeline:** + +```bash +# Install dependencies +pnpm install --frozen-lockfile + +# Build for staging +pnpm build:staging + +# Deploy (example with wrangler) +cd apps/main && pnpm deploy:staging +``` + +**Production Pipeline:** + +```bash +# Install dependencies +pnpm install --frozen-lockfile + +# Run all checks +pnpm lint +pnpm typecheck +pnpm test + +# Build for production +pnpm build:prod + +# Deploy (example with wrangler) +cd apps/main && pnpm deploy:prod +``` + +**Benefits:** + +- Turborepo caches builds per environment +- Environment-specific `.env` files are tracked as inputs +- Builds are only re-run when relevant files change + ## Additional Resources - [Turborepo Documentation](https://turbo.build/repo/docs) diff --git a/README.md b/README.md index 659a09e..3605441 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ pnpm dev:external # Run external app only # Building pnpm build # Build all apps pnpm build:apps # Build apps only +pnpm build:staging # Build main app for staging +pnpm build:prod # Build main app for production # Testing & Quality pnpm test # Run all tests diff --git a/apps/external/biome.json b/apps/external/biome.json index 3dc1855..7d53391 100644 --- a/apps/external/biome.json +++ b/apps/external/biome.json @@ -1,22 +1,9 @@ { "$schema": "https://biomejs.dev/schemas/2.2.5/schema.json", - "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, + "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": false }, "files": { - "includes": [ - "ui/src/**/*", - "ui/worker/**/*", - "ui/*.{ts,tsx,js,jsx,json}", - "api/src/**/*", - "api/*.{ts,tsx,js,jsx,json}", - "xtablo-expo/app/**/*", - "xtablo-expo/components/**/*", - "xtablo-expo/hooks/**/*", - "xtablo-expo/lib/**/*", - "xtablo-expo/providers/**/*", - "xtablo-expo/stores/**/*", - "xtablo-expo/types/**/*", - "xtablo-expo/*.{ts,tsx,js,jsx,json}" - ] + "ignoreUnknown": true, + "includes": ["src/**/*", "*.{ts,tsx,js,jsx,json}"] }, "formatter": { "enabled": true, @@ -278,14 +265,7 @@ } }, { - "includes": [ - "ui/src/**/*.{ts,tsx}", - "ui/worker/**/*.{ts,tsx}", - "ui/*.{ts,tsx}", - "api/src/**/*.{ts,tsx}", - "api/*.{ts,tsx}", - "xtablo-expo/**/*.{ts,tsx}" - ], + "includes": ["src/**/*.{ts,tsx}", "*.{ts,tsx}"], "linter": { "rules": { "complexity": { "noArguments": "error" }, @@ -314,29 +294,6 @@ } } } - }, - { - "includes": ["xtablo-expo/**/*.{js,jsx,ts,tsx}"], - "linter": { - "rules": { - "correctness": { - "noUndeclaredVariables": "off" - }, - "suspicious": { - "noExplicitAny": "warn" - } - } - } - }, - { - "includes": ["api/src/**/*.{js,ts}"], - "linter": { - "rules": { - "style": { - "noCommonJs": "off" - } - } - } } ] } diff --git a/apps/external/package.json b/apps/external/package.json index 59a0bb3..4ee632e 100644 --- a/apps/external/package.json +++ b/apps/external/package.json @@ -39,4 +39,3 @@ "zustand": "^5.0.5" } } - diff --git a/apps/external/src/CustomModal.tsx b/apps/external/src/CustomModal.tsx index 2aaa242..e19f466 100644 --- a/apps/external/src/CustomModal.tsx +++ b/apps/external/src/CustomModal.tsx @@ -1,5 +1,5 @@ -import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@xtablo/ui/components/dialog"; import { cn } from "@xtablo/shared"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@xtablo/ui/components/dialog"; // Custom Modal Component - now using shadcn/ui Dialog interface CustomModalProps { diff --git a/apps/external/src/EmbeddedBookingPage.tsx b/apps/external/src/EmbeddedBookingPage.tsx index be4e9c3..89ccaf9 100644 --- a/apps/external/src/EmbeddedBookingPage.tsx +++ b/apps/external/src/EmbeddedBookingPage.tsx @@ -1,16 +1,18 @@ -import { CustomModal } from "./CustomModal"; -import { LoadingSpinner } from "./LoadingSpinner"; +import { useCreateTabloWithOwner } from "@xtablo/shared"; +import { useSession } from "@xtablo/shared/contexts/SessionContext"; +import { useSignUpWithoutPassword } from "@xtablo/shared/hooks/auth"; +import { TimeSlot, usePublicSlots } from "@xtablo/shared/hooks/public"; +import { EventInsertInTablo } from "@xtablo/shared/types/events.types"; import { Button } from "@xtablo/ui/components/button"; import { FieldError } from "@xtablo/ui/components/field"; import { Input } from "@xtablo/ui/components/input"; import { Label } from "@xtablo/ui/components/label"; -import { Text, TypographyH3, TypographyH4, TypographyMuted } from "@xtablo/ui/components/typography"; -import { useSession } from "@xtablo/shared/contexts/SessionContext"; -import { useSignUpWithoutPassword } from "@xtablo/shared/hooks/auth"; -import { TimeSlot, usePublicSlots } from "@xtablo/shared/hooks/public"; -import { useCreateTabloWithOwner } from "@xtablo/shared"; -import { useMaybeUser } from "./UserStoreProvider"; -import { EventInsertInTablo } from "@xtablo/shared/types/events.types"; +import { + Text, + TypographyH3, + TypographyH4, + TypographyMuted, +} from "@xtablo/ui/components/typography"; import { CalendarIcon, ChevronLeftIcon, @@ -22,8 +24,11 @@ import { import { useState } from "react"; import { useParams, useSearchParams } from "react-router-dom"; import { twMerge } from "tailwind-merge"; -import { supabase } from "./lib/supabase"; +import { CustomModal } from "./CustomModal"; +import { LoadingSpinner } from "./LoadingSpinner"; import { api } from "./lib/api"; +import { supabase } from "./lib/supabase"; +import { useMaybeUser } from "./UserStoreProvider"; type ColorVariant = "black" | "white" | "blue" | "purple" | "green" | "orange" | "red"; @@ -209,19 +214,16 @@ export function EmbeddedBookingPage() { const shortUserId = userInfo?.substring(userInfo.lastIndexOf("-") + 1); - console.log({shortUserId, eventTypeStandardName}) + console.log({ shortUserId, eventTypeStandardName }); const { data: publicSlots, isLoading: isLoadingSlots } = usePublicSlots( api, shortUserId || "", eventTypeStandardName || "" ); - - const { mutateAsync: createTabloWithOwner } = useCreateTabloWithOwner(api, - () => { - handleCloseModal(); - } - ); + const { mutateAsync: createTabloWithOwner } = useCreateTabloWithOwner(api, () => { + handleCloseModal(); + }); const userProfile = publicSlots?.user; const eventType = publicSlots?.eventType; diff --git a/apps/external/src/FloatingBookingWidget.tsx b/apps/external/src/FloatingBookingWidget.tsx index 605f7fb..a29fe90 100644 --- a/apps/external/src/FloatingBookingWidget.tsx +++ b/apps/external/src/FloatingBookingWidget.tsx @@ -1,16 +1,13 @@ -import { CustomModal } from "./CustomModal"; -import { LoadingSpinner } from "./LoadingSpinner"; +import { useCreateTabloWithOwner } from "@xtablo/shared"; +import { useSession } from "@xtablo/shared/contexts/SessionContext"; +import { useSignUpWithoutPassword } from "@xtablo/shared/hooks/auth"; +import { TimeSlot, usePublicSlots } from "@xtablo/shared/hooks/public"; +import { EventInsertInTablo } from "@xtablo/shared/types/events.types"; import { Button } from "@xtablo/ui/components/button"; import { FieldError } from "@xtablo/ui/components/field"; import { Input } from "@xtablo/ui/components/input"; import { Label } from "@xtablo/ui/components/label"; import { Text, TypographyH4, TypographyMuted } from "@xtablo/ui/components/typography"; -import { useSession } from "@xtablo/shared/contexts/SessionContext"; -import { useSignUpWithoutPassword } from "@xtablo/shared/hooks/auth"; -import { TimeSlot, usePublicSlots } from "@xtablo/shared/hooks/public"; -// import { useCreateTabloWithOwner } from "@xtablo/shared"; -import { useMaybeUser } from "./UserStoreProvider"; -import { EventInsertInTablo } from "@xtablo/shared/types/events.types"; import { CalendarIcon, ChevronLeftIcon, @@ -23,9 +20,12 @@ import { import { useState } from "react"; import { useParams, useSearchParams } from "react-router-dom"; import { twMerge } from "tailwind-merge"; -import { supabase } from "./lib/supabase"; +import { CustomModal } from "./CustomModal"; +import { LoadingSpinner } from "./LoadingSpinner"; import { api } from "./lib/api"; -import { useCreateTabloWithOwner } from "@xtablo/shared"; +import { supabase } from "./lib/supabase"; +// import { useCreateTabloWithOwner } from "@xtablo/shared"; +import { useMaybeUser } from "./UserStoreProvider"; type ColorVariant = "black" | "white" | "blue" | "purple" | "green" | "orange" | "red"; @@ -132,12 +132,10 @@ export function FloatingBookingWidget() { eventTypeStandardName || "" ); - const { mutateAsync: createTabloWithOwner } = useCreateTabloWithOwner(api, - () => { - handleCloseModal(); - setIsWidgetOpen(false); - } - ); + const { mutateAsync: createTabloWithOwner } = useCreateTabloWithOwner(api, () => { + handleCloseModal(); + setIsWidgetOpen(false); + }); const userProfile = publicSlots?.user; const eventType = publicSlots?.eventType; diff --git a/apps/external/src/UserStoreProvider.tsx b/apps/external/src/UserStoreProvider.tsx index 429a40f..876625d 100644 --- a/apps/external/src/UserStoreProvider.tsx +++ b/apps/external/src/UserStoreProvider.tsx @@ -1,9 +1,9 @@ import { useQuery } from "@tanstack/react-query"; -import { LoadingSpinner } from "./LoadingSpinner"; import { useSession } from "@xtablo/shared/contexts/SessionContext"; import { Tables } from "@xtablo/shared/types/database.types"; import React from "react"; import { createStore, StoreApi, useStore } from "zustand"; +import { LoadingSpinner } from "./LoadingSpinner"; import { api } from "./lib/api"; export type User = Tables<"profiles"> & { @@ -12,7 +12,6 @@ export type User = Tables<"profiles"> & { const UserStoreContext = React.createContext | null>(null); - export const UserStoreProvider = ({ children }: { children: React.ReactNode }) => { const { session } = useSession(); const shouldFetchUser = !!session?.access_token; diff --git a/apps/external/src/main.css b/apps/external/src/main.css index 125ddee..a896ff7 100644 --- a/apps/external/src/main.css +++ b/apps/external/src/main.css @@ -301,8 +301,7 @@ transform: translate(-50%, -50%) rotate(0deg) translateX(150px) rotate(0deg); } 100% { - transform: translate(-50%, -50%) rotate(360deg) translateX(150px) - rotate(-360deg); + transform: translate(-50%, -50%) rotate(360deg) translateX(150px) rotate(-360deg); } } @@ -311,8 +310,7 @@ transform: translate(-50%, -50%) rotate(0deg) translateX(200px) rotate(0deg); } 100% { - transform: translate(-50%, -50%) rotate(-360deg) translateX(200px) - rotate(360deg); + transform: translate(-50%, -50%) rotate(-360deg) translateX(200px) rotate(360deg); } } @@ -321,8 +319,7 @@ transform: translate(-50%, -50%) rotate(0deg) translateX(100px) rotate(0deg); } 100% { - transform: translate(-50%, -50%) rotate(360deg) translateX(100px) - rotate(-360deg); + transform: translate(-50%, -50%) rotate(360deg) translateX(100px) rotate(-360deg); } } @@ -500,8 +497,7 @@ transform: translate(-50%, -50%) rotate(0deg) translateX(250px) rotate(0deg); } 100% { - transform: translate(-50%, -50%) rotate(360deg) translateX(250px) - rotate(-360deg); + transform: translate(-50%, -50%) rotate(360deg) translateX(250px) rotate(-360deg); } } @@ -510,8 +506,7 @@ transform: translate(-50%, -50%) rotate(0deg) translateX(120px) rotate(0deg); } 100% { - transform: translate(-50%, -50%) rotate(-360deg) translateX(120px) - rotate(360deg); + transform: translate(-50%, -50%) rotate(-360deg) translateX(120px) rotate(360deg); } } diff --git a/apps/external/src/main.tsx b/apps/external/src/main.tsx index 67fbe93..c79e82d 100644 --- a/apps/external/src/main.tsx +++ b/apps/external/src/main.tsx @@ -1,10 +1,9 @@ - import { QueryClientProvider } from "@tanstack/react-query"; -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; import { queryClient } from "@xtablo/shared"; import { ThemeProvider } from "@xtablo/shared/contexts/ThemeContext"; import { Toaster } from "@xtablo/ui/components/sonner"; +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; import { BrowserRouter as Router } from "react-router-dom"; import AppRoutes from "./routes"; diff --git a/apps/external/src/routes.tsx b/apps/external/src/routes.tsx index 018085d..311d609 100644 --- a/apps/external/src/routes.tsx +++ b/apps/external/src/routes.tsx @@ -1,6 +1,6 @@ -import { Routes, Route } from 'react-router-dom'; -import { EmbeddedBookingPage } from './EmbeddedBookingPage'; -import { FloatingBookingWidget } from './FloatingBookingWidget'; +import { Route, Routes } from "react-router-dom"; +import { EmbeddedBookingPage } from "./EmbeddedBookingPage"; +import { FloatingBookingWidget } from "./FloatingBookingWidget"; export default function AppRoutes() { return ( diff --git a/apps/external/tsconfig.json b/apps/external/tsconfig.json index 2ab9471..d6100fa 100644 --- a/apps/external/tsconfig.json +++ b/apps/external/tsconfig.json @@ -26,4 +26,3 @@ "include": ["src"], "references": [] } - diff --git a/apps/main/.biomeignore b/apps/main/.biomeignore new file mode 100644 index 0000000..5669c8d --- /dev/null +++ b/apps/main/.biomeignore @@ -0,0 +1,4 @@ +# Generated Wrangler files +worker-configuration.d.ts +worker/ + diff --git a/apps/main/biome.json b/apps/main/biome.json index 3dc1855..a8ec73b 100644 --- a/apps/main/biome.json +++ b/apps/main/biome.json @@ -1,22 +1,10 @@ { "$schema": "https://biomejs.dev/schemas/2.2.5/schema.json", - "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, + "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": false }, "files": { - "includes": [ - "ui/src/**/*", - "ui/worker/**/*", - "ui/*.{ts,tsx,js,jsx,json}", - "api/src/**/*", - "api/*.{ts,tsx,js,jsx,json}", - "xtablo-expo/app/**/*", - "xtablo-expo/components/**/*", - "xtablo-expo/hooks/**/*", - "xtablo-expo/lib/**/*", - "xtablo-expo/providers/**/*", - "xtablo-expo/stores/**/*", - "xtablo-expo/types/**/*", - "xtablo-expo/*.{ts,tsx,js,jsx,json}" - ] + "ignoreUnknown": true, + "maxSize": 10485760, + "includes": ["src/**/*"] }, "formatter": { "enabled": true, @@ -278,14 +266,7 @@ } }, { - "includes": [ - "ui/src/**/*.{ts,tsx}", - "ui/worker/**/*.{ts,tsx}", - "ui/*.{ts,tsx}", - "api/src/**/*.{ts,tsx}", - "api/*.{ts,tsx}", - "xtablo-expo/**/*.{ts,tsx}" - ], + "includes": ["src/**/*.{ts,tsx}", "worker/**/*.{ts,tsx}", "*.{ts,tsx}"], "linter": { "rules": { "complexity": { "noArguments": "error" }, @@ -314,29 +295,6 @@ } } } - }, - { - "includes": ["xtablo-expo/**/*.{js,jsx,ts,tsx}"], - "linter": { - "rules": { - "correctness": { - "noUndeclaredVariables": "off" - }, - "suspicious": { - "noExplicitAny": "warn" - } - } - } - }, - { - "includes": ["api/src/**/*.{js,ts}"], - "linter": { - "rules": { - "style": { - "noCommonJs": "off" - } - } - } } ] } diff --git a/apps/main/package.json b/apps/main/package.json index 9fcd83e..25414fe 100644 --- a/apps/main/package.json +++ b/apps/main/package.json @@ -13,8 +13,7 @@ "preview": "vite preview", "build:staging": "tsc -b && vite build --mode staging", "build:prod": "tsc -b && vite build --mode production", - "deploy:staging": "tsc -b && vite build --mode staging && wrangler deploy --env=\"\"", - "deploy:prod": "tsc -b && vite build --mode production && wrangler deploy --env=\"\"", + "deploy": "wrangler deploy --env=\"\"", "cf-typegen": "wrangler types", "test": "vitest run --mode dev --passWithNoTests", "test:watch": "vitest watch --passWithNoTests", diff --git a/apps/main/src/App.tsx b/apps/main/src/App.tsx index 57cd411..be4d938 100644 --- a/apps/main/src/App.tsx +++ b/apps/main/src/App.tsx @@ -1,11 +1,11 @@ -import { Toaster } from "@xtablo/ui/components/sonner"; import { SessionProvider } from "@xtablo/shared/contexts/SessionContext"; import { ThemeProvider } from "@xtablo/shared/contexts/ThemeContext"; -import { UserStoreProvider } from "./providers/UserStoreProvider"; -import { routes } from "./lib/routes"; -import { DatadogRumProvider } from "./providers/DatadogRumProvider"; +import { Toaster } from "@xtablo/ui/components/sonner"; import { BrowserRouter as Router, useRoutes } from "react-router-dom"; +import { routes } from "./lib/routes"; import { supabase } from "./lib/supabase"; +import { DatadogRumProvider } from "./providers/DatadogRumProvider"; +import { UserStoreProvider } from "./providers/UserStoreProvider"; const AppRoutes = () => { const element = useRoutes(routes); diff --git a/apps/main/src/components/AuthenticationGateway.tsx b/apps/main/src/components/AuthenticationGateway.tsx index d411f10..4c8e124 100644 --- a/apps/main/src/components/AuthenticationGateway.tsx +++ b/apps/main/src/components/AuthenticationGateway.tsx @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; import { Navigate, Outlet, useSearchParams } from "react-router-dom"; import { match } from "ts-pattern"; -import { LoadingSpinner } from "./LoadingSpinner"; import { useMaybeUser } from "../providers/UserStoreProvider"; +import { LoadingSpinner } from "./LoadingSpinner"; export const AuthenticationGateway = () => { const user = useMaybeUser(); diff --git a/apps/main/src/components/AuthenticationGateway.unit.tsx b/apps/main/src/components/AuthenticationGateway.unit.tsx index 7478c50..ee07143 100644 --- a/apps/main/src/components/AuthenticationGateway.unit.tsx +++ b/apps/main/src/components/AuthenticationGateway.unit.tsx @@ -1,26 +1,28 @@ import { screen, waitFor } from "@testing-library/react"; import { AuthenticationGateway } from "@ui/components/AuthenticationGateway"; import { SessionTestProvider } from "@xtablo/shared/contexts/SessionContext"; -import { renderWithRouter } from "../utils/testHelpers"; import { Route, Routes } from "react-router-dom"; +import { renderWithRouter } from "../utils/testHelpers"; describe("PublicRoute", () => { it("shows loading state initially", () => { renderWithRouter( - + }> Login Page} /> diff --git a/apps/main/src/components/BrandButtons/LoginWithGoogle.test.tsx b/apps/main/src/components/BrandButtons/LoginWithGoogle.test.tsx index 3f155e5..299ecba 100644 --- a/apps/main/src/components/BrandButtons/LoginWithGoogle.test.tsx +++ b/apps/main/src/components/BrandButtons/LoginWithGoogle.test.tsx @@ -1,7 +1,7 @@ import { fireEvent, render, screen } from "@testing-library/react"; import { LoginWithGoogle } from "@ui/components/BrandButtons/LoginWithGoogle"; -import { useLoginGoogle } from "../../hooks/auth"; import { vi } from "vitest"; +import { useLoginGoogle } from "../../hooks/auth"; vi.mock("../../hooks/auth", () => ({ useLoginGoogle: vi.fn(), diff --git a/apps/main/src/components/ChannelPreview.tsx b/apps/main/src/components/ChannelPreview.tsx index e3a9294..b464145 100644 --- a/apps/main/src/components/ChannelPreview.tsx +++ b/apps/main/src/components/ChannelPreview.tsx @@ -1,6 +1,6 @@ import { ChannelBadge } from "@ui/components/ChannelBadge"; -import { Badge } from "@xtablo/ui/components/badge"; import { UserTablo } from "@xtablo/shared/types/tablos.types"; +import { Badge } from "@xtablo/ui/components/badge"; import { ReactNode } from "react"; import { Channel } from "stream-chat"; import { twMerge } from "tailwind-merge"; diff --git a/apps/main/src/components/ClickOutside.tsx b/apps/main/src/components/ClickOutside.tsx index 6d5d5d9..f39778d 100644 --- a/apps/main/src/components/ClickOutside.tsx +++ b/apps/main/src/components/ClickOutside.tsx @@ -1,5 +1,5 @@ -import React from "react"; import { useClickOutside } from "@xtablo/shared/hooks/useClickOutside"; +import React from "react"; interface ClickOutsideProps { children: React.ReactNode; diff --git a/apps/main/src/components/CustomModal.tsx b/apps/main/src/components/CustomModal.tsx index 2aaa242..e19f466 100644 --- a/apps/main/src/components/CustomModal.tsx +++ b/apps/main/src/components/CustomModal.tsx @@ -1,5 +1,5 @@ -import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@xtablo/ui/components/dialog"; import { cn } from "@xtablo/shared"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@xtablo/ui/components/dialog"; // Custom Modal Component - now using shadcn/ui Dialog interface CustomModalProps { diff --git a/apps/main/src/components/EmbedConfigModal.tsx b/apps/main/src/components/EmbedConfigModal.tsx index 257e6c8..3f2b025 100644 --- a/apps/main/src/components/EmbedConfigModal.tsx +++ b/apps/main/src/components/EmbedConfigModal.tsx @@ -1,4 +1,5 @@ import { Button } from "@xtablo/ui/components/button"; +import { CopyButton } from "@xtablo/ui/components/clipboard"; import { Dialog, DialogContent, @@ -14,9 +15,8 @@ import { SelectTrigger, SelectValue, } from "@xtablo/ui/components/select"; -import { CopyButton } from "@xtablo/ui/components/clipboard"; -import { useState } from "react"; import { TypographyMuted, TypographyP } from "@xtablo/ui/components/typography"; +import { useState } from "react"; type ColorVariant = "black" | "white" | "blue" | "purple" | "green" | "orange" | "red"; diff --git a/apps/main/src/components/EventDetailsModal.tsx b/apps/main/src/components/EventDetailsModal.tsx index 75546f5..b2586a7 100644 --- a/apps/main/src/components/EventDetailsModal.tsx +++ b/apps/main/src/components/EventDetailsModal.tsx @@ -1,6 +1,6 @@ +import { EventAndTablo } from "@xtablo/shared/types/events.types"; import { Button } from "@xtablo/ui/components/button"; import { Strong, Text } from "@xtablo/ui/components/typography"; -import { EventAndTablo } from "@xtablo/shared/types/events.types"; import { CalendarIcon, User } from "lucide-react"; import { twMerge } from "tailwind-merge"; import { CustomModal } from "./CustomModal"; diff --git a/apps/main/src/components/EventModal.tsx b/apps/main/src/components/EventModal.tsx index 869c7c3..e224ca5 100644 --- a/apps/main/src/components/EventModal.tsx +++ b/apps/main/src/components/EventModal.tsx @@ -1,4 +1,5 @@ import { getLocalTimeZone, parseDate, today } from "@internationalized/date"; +import { Event, EventInsert } from "@xtablo/shared/types/events.types"; import { Button } from "@xtablo/ui/components/button"; import { DatePicker } from "@xtablo/ui/components/date-picker"; import { @@ -20,12 +21,11 @@ import { } from "@xtablo/ui/components/select"; import { Textarea } from "@xtablo/ui/components/textarea"; import { TimeInput } from "@xtablo/ui/components/time-input"; +import { useEffect, useState } from "react"; +import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { useCreateEvents, useEvent, useUpdateEvent } from "../hooks/events"; import { useTablosList } from "../hooks/tablos"; import { useUser } from "../providers/UserStoreProvider"; -import { Event, EventInsert } from "@xtablo/shared/types/events.types"; -import { useEffect, useState } from "react"; -import { useNavigate, useParams, useSearchParams } from "react-router-dom"; export const EventModal = ({ mode }: { mode: "create" | "edit" }) => { const { event_id } = useParams(); diff --git a/apps/main/src/components/EventTypeCard.tsx b/apps/main/src/components/EventTypeCard.tsx index edc6e02..7bf3768 100644 --- a/apps/main/src/components/EventTypeCard.tsx +++ b/apps/main/src/components/EventTypeCard.tsx @@ -1,3 +1,4 @@ +import { EmbedConfigModal, EmbedType } from "@ui/components/EmbedConfigModal"; import { Button } from "@xtablo/ui/components/button"; import { Card, @@ -7,8 +8,6 @@ import { CardHeader, CardTitle, } from "@xtablo/ui/components/card"; -import { EmbedConfigModal, EmbedType } from "@ui/components/EmbedConfigModal"; -import { EventType, EventTypeConfig, useEventTypes } from "../hooks/event-types"; import { CheckIcon, EditIcon, @@ -18,8 +17,9 @@ import { XIcon, } from "lucide-react"; import { useState } from "react"; -import { useUser } from "../providers/UserStoreProvider"; import { match } from "ts-pattern"; +import { EventType, EventTypeConfig, useEventTypes } from "../hooks/event-types"; +import { useUser } from "../providers/UserStoreProvider"; export function EventTypeCard({ eventType, diff --git a/apps/main/src/components/ExceptionModal.tsx b/apps/main/src/components/ExceptionModal.tsx index be4c0cf..4ebb8eb 100644 --- a/apps/main/src/components/ExceptionModal.tsx +++ b/apps/main/src/components/ExceptionModal.tsx @@ -1,4 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod"; +import { toast } from "@xtablo/shared"; import { Button } from "@xtablo/ui/components/button"; import { ButtonGroup } from "@xtablo/ui/components/button-group"; import { DatePickerV1 } from "@xtablo/ui/components/date-picker"; @@ -12,11 +13,10 @@ import { } from "@xtablo/ui/components/dialog"; import { Label } from "@xtablo/ui/components/label"; import { TimeInput } from "@xtablo/ui/components/time-input"; -import { Exception } from "../hooks/availabilities"; -import { toast } from "@xtablo/shared"; import { PlusIcon } from "lucide-react"; import { Controller, useForm } from "react-hook-form"; import * as z from "zod"; +import { Exception } from "../hooks/availabilities"; const formSchema = z.object({ exceptionType: z.enum(["day", "hours"]), diff --git a/apps/main/src/components/ImageCropDialog.tsx b/apps/main/src/components/ImageCropDialog.tsx index ee44165..7467e9c 100644 --- a/apps/main/src/components/ImageCropDialog.tsx +++ b/apps/main/src/components/ImageCropDialog.tsx @@ -1,6 +1,4 @@ -import { useState, useCallback } from "react"; -import Cropper from "react-easy-crop"; -import type { Area } from "react-easy-crop"; +import { Button } from "@xtablo/ui/components/button"; import { Dialog, DialogContent, @@ -9,9 +7,11 @@ import { DialogHeader, DialogTitle, } from "@xtablo/ui/components/dialog"; -import { Button } from "@xtablo/ui/components/button"; -import { Slider } from "@xtablo/ui/components/slider"; import { Label } from "@xtablo/ui/components/label"; +import { Slider } from "@xtablo/ui/components/slider"; +import { useCallback, useState } from "react"; +import type { Area } from "react-easy-crop"; +import Cropper from "react-easy-crop"; interface ImageCropDialogProps { open: boolean; diff --git a/apps/main/src/components/ImportICSModal.tsx b/apps/main/src/components/ImportICSModal.tsx index a2ab57f..4d21c35 100644 --- a/apps/main/src/components/ImportICSModal.tsx +++ b/apps/main/src/components/ImportICSModal.tsx @@ -1,3 +1,6 @@ +import { ParsedICSEvent, parseICSFile, toast } from "@xtablo/shared"; +import { EventInsert } from "@xtablo/shared/types/events.types"; +import { CreateTablo } from "@xtablo/shared/types/tablos.types"; import { Select, SelectContent, @@ -5,14 +8,10 @@ import { SelectTrigger, SelectValue, } from "@xtablo/ui/components/select"; +import { useRef, useState } from "react"; import { useCreateEvents } from "../hooks/events"; import { useCreateTablo, useTablosList } from "../hooks/tablos"; -import { toast } from "@xtablo/shared"; import { useUser } from "../providers/UserStoreProvider"; -import { EventInsert } from "@xtablo/shared/types/events.types"; -import { CreateTablo } from "@xtablo/shared/types/tablos.types"; -import { ParsedICSEvent, parseICSFile } from "@xtablo/shared"; -import { useRef, useState } from "react"; interface ImportICSModalProps { onClose: () => void; diff --git a/apps/main/src/components/Layout.test.tsx b/apps/main/src/components/Layout.test.tsx index c4d0315..ee4db66 100644 --- a/apps/main/src/components/Layout.test.tsx +++ b/apps/main/src/components/Layout.test.tsx @@ -1,8 +1,8 @@ import { fireEvent, render, screen } from "@testing-library/react"; import { Layout } from "@ui/components/Layout"; -import { SessionTestProvider } from "@xtablo/shared/contexts/SessionContext"; -import { renderWithProviders } from "../utils/testHelpers"; +import { SessionTestProvider } from "@xtablo/shared/contexts/SessionContext"; import { BrowserRouter } from "react-router-dom"; +import { renderWithProviders } from "../utils/testHelpers"; describe("Layout", () => { it("renders the layout with children", () => { diff --git a/apps/main/src/components/NavigationBar.tsx b/apps/main/src/components/NavigationBar.tsx index bf312ce..dd26e40 100644 --- a/apps/main/src/components/NavigationBar.tsx +++ b/apps/main/src/components/NavigationBar.tsx @@ -1,16 +1,17 @@ // shadcn components -import { - Avatar, - AvatarBadge, - AvatarFallback, - AvatarImage, -} from "@xtablo/ui/components/avatar"; + +import { cn } from "@xtablo/shared/lib/cn.ts"; +import { Avatar, AvatarBadge, AvatarFallback, AvatarImage } from "@xtablo/ui/components/avatar"; import { Button } from "@xtablo/ui/components/button"; -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@xtablo/ui/components/dropdown-menu"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@xtablo/ui/components/dropdown-menu"; import { TypographyLarge, TypographyMuted } from "@xtablo/ui/components/typography"; -import { useUser } from "../providers/UserStoreProvider"; -import { isProd, isStaging } from "../lib/env"; -import { getXtabloIcon } from "../utils/iconHelpers"; +import { cva, type VariantProps } from "class-variance-authority"; import { CalendarCheckIcon, CalendarIcon, @@ -31,10 +32,11 @@ import { useState } from "react"; import { Separator } from "react-aria-components"; import { Link as RouterLink, useLocation } from "react-router-dom"; import { twMerge } from "tailwind-merge"; -import { ThemeSwitcher } from "./ThemeSwitcher"; -import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@xtablo/shared/lib/cn.ts"; import { useLogout } from "../hooks/auth"; +import { isProd, isStaging } from "../lib/env"; +import { useUser } from "../providers/UserStoreProvider"; +import { getXtabloIcon } from "../utils/iconHelpers"; +import { ThemeSwitcher } from "./ThemeSwitcher"; type NavLinkItem = { isActive?: boolean; @@ -161,7 +163,7 @@ export function UserMenuPopover({ isCollapsed }: { isCollapsed: boolean }) { - +