Skip to content
Open
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
25 changes: 25 additions & 0 deletions app/(dashboard)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ import { DashboardRecentActivity } from "@/components/app/dashboard-recent-activ



/**
* Aggregates and returns the most recent credentials, cards, and secrets as unified recent activity items.
*
* Maps each credential, card, and secret to a standardized `RecentItem` format, tags them by type, merges them, sorts by most recent activity, and limits the result to the maximum allowed recent items.
*
* @param usersResponse - The response containing credential data
* @param cardsResponse - The response containing card data
* @param secretsResponse - The response containing secret data
* @returns An array of the most recent activity items across all types
*/
async function getRecentItems(
usersResponse: ListCredentialsOutput,
cardsResponse: ListCardsOutput,
Expand Down Expand Up @@ -59,6 +69,14 @@ export const metadata: Metadata = {
title: "Dashboard Overview",
}

/**
* Computes the total counts of credentials, cards, and secrets from the provided data.
*
* @param credentialsData - The response data containing the list of credentials
* @param cardsData - The response data containing the list of cards
* @param secretsData - The response data containing the list of secrets
* @returns An object with the counts of credentials, cards, and secrets
*/
async function getStats(
credentialsData: ListCredentialsOutput,
cardsData: ListCardsOutput,
Expand All @@ -71,6 +89,13 @@ async function getStats(
}
}

/**
* Renders the dashboard overview page with recent activity and statistics for credentials, cards, and secrets.
*
* Fetches recent credentials, cards, and secrets, computes their counts, and displays them using overview and recent activity components.
*
* @returns The dashboard page JSX element
*/
export default async function DashboardPage() {
const context = await createContext()
const serverClient = createServerClient(context)
Expand Down
5 changes: 5 additions & 0 deletions app/(marketing)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { MarketingWaitlistForm } from "@/components/app/marketing-waitlist-form"
import { StatCard } from "@/components/shared/stat-card"
import { AnimatedGridPattern } from "@/components/ui/animated-grid-pattern"

/**
* Renders the marketing landing page with dynamic statistics and a waitlist form.
*
* Fetches waitlist, user, and encrypted data counts from the server and displays them alongside marketing content, headers, and an animated background.
*/
export default async function Home() {
const serverClient = createServerClient({
session: null,
Expand Down
6 changes: 6 additions & 0 deletions app/api/orpc/[[...rest]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import { RPCHandler } from "@orpc/server/fetch"

const handler = new RPCHandler(appRouter)

/**
* Handles incoming HTTP requests for the oRPC API route, delegating them to the RPC handler and returning the appropriate response.
*
* @param request - The incoming HTTP request to process
* @returns The response generated by the RPC handler, or a 404 response if no response is produced
*/
async function handleRequest(request: Request) {
const { response } = await handler.handle(request, {
prefix: "/api/orpc",
Expand Down
9 changes: 9 additions & 0 deletions components/app/dashboard-add-card-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ interface CardDialogProps {
availableTags?: TagDto[]
}

/**
* Renders a dialog for securely adding a new card, encrypting sensitive data before submission.
*
* Presents a form for entering card details, encrypts the card number and CVV client-side, and submits the encrypted data to create a new card. Handles form validation, error and success notifications, and supports adding multiple cards in sequence. Resets form and sensitive data state when the dialog is closed.
*
* @param open - Whether the dialog is open
* @param onOpenChange - Callback invoked when the dialog open state changes
* @param availableTags - Optional list of tags that can be assigned to the card
*/
export function DashboardAddCardDialog({
open,
onOpenChange,
Expand Down
8 changes: 8 additions & 0 deletions components/app/dashboard-add-credential-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ interface CredentialDialogProps {
onOpenChange: (open: boolean) => void
}

/**
* Displays a dialog for adding a new credential with optional metadata to the user's vault.
*
* Provides a form for entering credential details, password management (including generation, strength checking, and clipboard copy), and an expandable section for additional metadata. Handles form validation, encryption of sensitive data, and submission via a mutation hook. On successful creation, optionally resets the form for consecutive entries or closes the dialog.
*
* @param open - Whether the dialog is visible
* @param onOpenChange - Callback invoked when the dialog's open state changes
*/
export function DashboardAddCredentialDialog({
open,
onOpenChange,
Expand Down
8 changes: 8 additions & 0 deletions components/app/dashboard-add-secret-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ interface SecretDialogProps {
onOpenChange: (open: boolean) => void
}

/**
* Displays a dialog for securely adding new secrets to a vault, handling client-side encryption and form validation.
*
* Presents a form for entering secret data, encrypts each secret on the client before submission, and supports creating multiple secrets in sequence. On successful creation, shows a confirmation and resets the form if desired; on failure, displays an error message.
*
* @param open - Whether the dialog is visible
* @param onOpenChange - Callback to update the dialog's open state
*/
export function DashboardAddSecretDialog({
open,
onOpenChange,
Expand Down
5 changes: 5 additions & 0 deletions components/app/dashboard-overview-stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ interface OverviewStatsProps {
}
}

/**
* Displays an overview of account, payment card, and secure note statistics in a responsive grid layout.
*
* @param stats - Object containing counts for credentials, cards, and secrets to display in the overview.
*/
export async function OverviewStats({ stats }: OverviewStatsProps) {
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
Expand Down
7 changes: 7 additions & 0 deletions components/app/marketing-waitlist-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ import {
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"

/**
* Renders a waitlist signup form for users to join by submitting their email address.
*
* Displays the current number of people who have joined the waitlist and provides feedback on successful or failed submissions.
*
* @param count - The number of people who have already joined the waitlist
*/
export function MarketingWaitlistForm({ count }: { count: number }) {
const joinWaitlistMutation = useJoinWaitlist()

Expand Down
7 changes: 7 additions & 0 deletions components/layout/layout-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ThemeProvider } from "@/components/layout/theme-provider"
import { TooltipProvider } from "@/components/ui/tooltip"

/**
* Provides global context providers for query management, theming, and tooltips to its child components.
*
* Wraps children with `QueryClientProvider`, `ThemeProvider`, and `TooltipProvider`, configuring default query options and theming behavior for the application.
*
* @param children - The React nodes to be rendered within the context providers
*/
export function LayoutWrapper({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(
() =>
Expand Down
6 changes: 6 additions & 0 deletions lib/utils/encryption-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { database } from "@/prisma/client"
import type { EncryptedDataDto } from "@/schemas/encryption/encryption"

/**
* Creates a new encrypted data record in the database using the provided initialization vector, encrypted value, and encryption key.
*
* @param data - The encrypted data details to store
* @returns An object indicating success and the new record's ID if successful, or an error message if the operation fails
*/
export async function createEncryptedData(data: EncryptedDataDto): Promise<{
success: boolean
encryptedData?: { id: string }
Expand Down
7 changes: 7 additions & 0 deletions lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ import { PRIORITY_ACTIVITY_TYPE } from "@/config/consts"
export * from "./card-expiry-helpers"
export * from "./password-helpers"

/**
* Merges multiple class name values into a single string, resolving Tailwind CSS conflicts.
*
* Accepts any combination of class name arguments, arrays, or objects, and returns a deduplicated, merged class string suitable for use in JSX.
*
* @returns The merged class name string
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Expand Down
10 changes: 10 additions & 0 deletions lib/utils/tag-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { database } from "@/prisma/client"
import type { TagDto } from "@/schemas/utils/tag"
import type { Prisma } from "@prisma/client"

/**
* Finds or creates tags for a user and optional container, returning a Prisma input object for connecting these tags.
*
* For each tag in the input array, ensures a tag with the same name, user ID, and container ID exists in the database, creating it if necessary. Returns an object suitable for nested tag connections in Prisma.
*
* @param tags - Array of tag data objects to find or create
* @param userId - The ID of the user to associate with the tags
* @param containerId - Optional container ID to further scope the tags
* @returns Prisma input object with a `connect` property containing tag IDs
*/
export async function createTagsAndGetConnections(
tags: TagDto[],
userId: string,
Expand Down
8 changes: 6 additions & 2 deletions orpc/client/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import { appRouter } from "../routers"
import type { ORPCContext } from "../types"

/**
* Server-side oRPC client for SSR optimization
* This eliminates HTTP requests during server-side rendering
* Creates a server-side oRPC client instance for the app router using the provided SSR context.
*
* This client enables direct invocation of router procedures during server-side rendering, bypassing HTTP requests.
*
* @param context - The server-side rendering context to be used for procedure calls
* @returns A router client instance bound to the app router and the given context
*/
export function createServerClient(
context: ORPCContext
Expand Down
7 changes: 7 additions & 0 deletions orpc/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { auth } from "@/lib/auth/server"

import type { ORPCContext } from "./types"

/**
* Creates an ORPC context containing the current session and user information.
*
* Attempts to retrieve authentication data using the current request headers. If authentication fails or no session is found, both `session` and `user` are set to `null`.
*
* @returns An object with `session` and `user` properties representing the current authentication state.
*/
export async function createContext(): Promise<ORPCContext> {
try {
const authResult = await auth.api.getSession({
Expand Down
39 changes: 34 additions & 5 deletions orpc/hooks/use-cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ export const cardKeys = {
detail: (id: string) => [...cardKeys.details(), id] as const,
}

// Get single card
/**
* Fetches a single card by its ID using React Query.
*
* The query is enabled only if a valid ID is provided.
*
* @param id - The unique identifier of the card to fetch
* @returns The React Query result for the requested card
*/
export function useCard(id: string) {
return useQuery(
orpc.cards.get.queryOptions({
Expand All @@ -31,7 +38,12 @@ export function useCard(id: string) {
)
}

// List cards with pagination
/**
* Fetches a paginated list of cards, optionally filtered by the provided input.
*
* @param input - Pagination and filter options for listing cards. Defaults to the first page with a limit of 10 cards.
* @returns The query result containing the list of cards and pagination metadata.
*/
export function useCards(input: ListCardsInput = { page: 1, limit: 10 }) {
return useQuery(
orpc.cards.list.queryOptions({
Expand All @@ -41,7 +53,11 @@ export function useCards(input: ListCardsInput = { page: 1, limit: 10 }) {
)
}

// Create card mutation
/**
* Returns a mutation hook for creating a new card.
*
* On success, invalidates cached card lists and adds the new card to the cache.
*/
export function useCreateCard() {
const queryClient = useQueryClient()

Expand All @@ -64,7 +80,13 @@ export function useCreateCard() {
)
}

// Update card mutation
/**
* Returns a mutation hook for updating an existing card with optimistic cache updates.
*
* Performs an optimistic update of the card data in the cache before the server response, rolling back on error and updating with the server response on success. Also invalidates card list queries to ensure fresh data.
*
* @returns A mutation hook for updating card data.
*/
export function useUpdateCard() {
const queryClient = useQueryClient()

Expand Down Expand Up @@ -120,7 +142,14 @@ export function useUpdateCard() {
)
}

// Delete card mutation
/**
* Provides a mutation hook to delete a card, performing optimistic cache removal and restoring the cache if the deletion fails.
*
* On successful deletion, invalidates cached card lists to ensure updated data is fetched.
* If the deletion fails, restores the previously cached card data.
*
* @returns A mutation object for deleting a card by ID
*/
export function useDeleteCard() {
const queryClient = useQueryClient()

Expand Down
46 changes: 40 additions & 6 deletions orpc/hooks/use-containers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import type {
} from "@/schemas/utils/dto"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"

// Get single container
/**
* Retrieves a single container by its ID using a React Query hook.
*
* The query is enabled only if a valid ID is provided.
*
* @param id - The unique identifier of the container to fetch
* @returns The query result containing the container data
*/
export function useContainer(id: string) {
return useQuery(
orpc.containers.get.queryOptions({
Expand All @@ -20,7 +27,12 @@ export function useContainer(id: string) {
)
}

// List containers with pagination
/**
* Fetches a paginated list of containers using the provided pagination input.
*
* @param input - Pagination parameters for listing containers. Defaults to page 1 and limit 10.
* @returns A React Query result containing the list of containers.
*/
export function useContainers(
input: ListContainersInput = { page: 1, limit: 10 }
) {
Expand All @@ -32,7 +44,12 @@ export function useContainers(
)
}

// Create container mutation
/**
* Provides a mutation hook to create a new container and update the cache.
*
* On successful creation, the container list is invalidated and the new container is cached. Errors are logged to the console.
* @returns A mutation object for creating containers.
*/
export function useCreateContainer() {
const queryClient = useQueryClient()

Expand All @@ -55,7 +72,12 @@ export function useCreateContainer() {
)
}

// Create container with secrets mutation
/**
* Provides a mutation hook to create a new container along with its secrets.
*
* On successful creation, invalidates the containers and secrets list queries and caches the new container. Logs errors to the console if the operation fails.
* @returns A mutation object for creating a container with secrets.
*/
export function useCreateContainerWithSecrets() {
const queryClient = useQueryClient()

Expand Down Expand Up @@ -87,7 +109,13 @@ export function useCreateContainerWithSecrets() {
)
}

// Update container mutation
/**
* Provides a mutation hook to update an existing container with optimistic cache updates and rollback on error.
*
* On mutation, the cache is optimistically updated with the new input. If the update fails, the cache is restored to its previous state. On success, the cache is updated with the server response and the container list query is invalidated to ensure fresh data.
*
* @returns A mutation object for updating a container.
*/
export function useUpdateContainer() {
const queryClient = useQueryClient()

Expand Down Expand Up @@ -141,7 +169,13 @@ export function useUpdateContainer() {
)
}

// Delete container mutation
/**
* Provides a mutation hook to delete a container and manage cache updates optimistically.
*
* Removes the container from the cache before the server confirms deletion, restoring it if the operation fails. On success, invalidates the container list query to refresh data.
*
* @returns A mutation object for deleting containers.
*/
export function useDeleteContainer() {
const queryClient = useQueryClient()

Expand Down
Loading