Refactored a bunch of shit
All checks were successful
Docker Deploy / build-and-push (push) Successful in 3m57s
All checks were successful
Docker Deploy / build-and-push (push) Successful in 3m57s
This commit is contained in:
@@ -25,3 +25,16 @@ export function formatTimeRange(start: Date, end: Date | null): string {
|
||||
const ms = end.getTime() - start.getTime();
|
||||
return formatDuration(ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a cent-based amount as a currency string.
|
||||
* @param amount - Amount in cents (e.g. 1500 = $15.00)
|
||||
* @param currency - ISO 4217 currency code (default: 'USD')
|
||||
* @returns Formatted currency string like "$15.00"
|
||||
*/
|
||||
export function formatCurrency(amount: number, currency: string = "USD"): string {
|
||||
return new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: currency,
|
||||
}).format(amount / 100);
|
||||
}
|
||||
|
||||
24
src/lib/getCurrentTeam.ts
Normal file
24
src/lib/getCurrentTeam.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { db } from '../db';
|
||||
import { members } from '../db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
type User = { id: string; [key: string]: any };
|
||||
|
||||
/**
|
||||
* Get the current team membership for a user based on the currentTeamId cookie.
|
||||
* Returns the membership row, or null if the user has no memberships.
|
||||
*/
|
||||
export async function getCurrentTeam(user: User, currentTeamId?: string | null) {
|
||||
const userMemberships = await db.select()
|
||||
.from(members)
|
||||
.where(eq(members.userId, user.id))
|
||||
.all();
|
||||
|
||||
if (userMemberships.length === 0) return null;
|
||||
|
||||
const membership = currentTeamId
|
||||
? userMemberships.find(m => m.organizationId === currentTeamId) || userMemberships[0]
|
||||
: userMemberships[0];
|
||||
|
||||
return membership;
|
||||
}
|
||||
@@ -2,6 +2,30 @@ import { db } from "../db";
|
||||
import { clients, tags as tagsTable } from "../db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
export const MAX_LENGTHS = {
|
||||
name: 255,
|
||||
email: 320,
|
||||
password: 128,
|
||||
phone: 50,
|
||||
address: 255, // street, city, state, zip, country
|
||||
currency: 10,
|
||||
invoiceNumber: 50,
|
||||
invoiceNotes: 5000,
|
||||
itemDescription: 2000,
|
||||
description: 2000, // time entry description
|
||||
} as const;
|
||||
|
||||
export function exceedsLength(
|
||||
field: string,
|
||||
value: string | null | undefined,
|
||||
maxLength: number,
|
||||
): string | null {
|
||||
if (value && value.length > maxLength) {
|
||||
return `${field} must be ${maxLength} characters or fewer`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function validateTimeEntryResources({
|
||||
organizationId,
|
||||
clientId,
|
||||
@@ -60,3 +84,9 @@ export function validateTimeRange(
|
||||
|
||||
return { valid: true, startDate, endDate };
|
||||
}
|
||||
|
||||
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
export function isValidEmail(email: string): boolean {
|
||||
return EMAIL_REGEX.test(email) && email.length <= 320;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user