Version 1.0 squash
This commit is contained in:
51
web/src/lib/pocketbase.ts
Normal file
51
web/src/lib/pocketbase.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* PocketBase Client Configuration
|
||||
*
|
||||
* Singleton PocketBase client instance with automatic environment detection.
|
||||
* Connects to local instance in development, production URL in production.
|
||||
*/
|
||||
|
||||
import PocketBase from "pocketbase";
|
||||
|
||||
/**
|
||||
* PocketBase URL configuration based on environment
|
||||
* Development: Local PocketBase instance (http://127.0.0.1:8090)
|
||||
* Production: Production PocketBase instance
|
||||
*
|
||||
* @returns PocketBase API base URL
|
||||
*/
|
||||
const getPocketBaseUrl = (): string => {
|
||||
const isDev = import.meta.env.MODE === "development";
|
||||
|
||||
if (isDev) {
|
||||
return "http://localhost:8090";
|
||||
}
|
||||
|
||||
/* Production URL - configure via build process or use relative URL */
|
||||
return import.meta.env.VITE_POCKETBASE_URL || "http://localhost:8090";
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton PocketBase client instance
|
||||
* Import this instance throughout the application for all PocketBase operations
|
||||
*/
|
||||
export const pb = new PocketBase(getPocketBaseUrl());
|
||||
|
||||
/**
|
||||
* Disable auto-cancellation to prevent request interruptions
|
||||
* By default, PocketBase cancels pending requests when new ones are made,
|
||||
* which can cause issues with concurrent requests and real-time subscriptions
|
||||
*/
|
||||
pb.autoCancellation(false);
|
||||
|
||||
/**
|
||||
* PocketBase collection names
|
||||
* Centralized collection name constants to avoid typos and enable refactoring
|
||||
*/
|
||||
export const Collections = {
|
||||
About: "about",
|
||||
Companies: "companies",
|
||||
Notices: "notices",
|
||||
FloorPlans: "floor_plans",
|
||||
Rooms: "rooms",
|
||||
} as const;
|
||||
65
web/src/lib/result.ts
Normal file
65
web/src/lib/result.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Result Utility
|
||||
*
|
||||
* Go-style error handling utilities for safer, more explicit error handling.
|
||||
* Provides Result type and tryCatch wrappers to eliminate try-catch boilerplate.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Go-style Result type for explicit error handling
|
||||
* Returns [data, null] on success or [null, error] on failure
|
||||
*
|
||||
* @example
|
||||
* const [data, error] = tryCatch(() => riskyOperation());
|
||||
* if (error) {
|
||||
* // Handle error
|
||||
* return;
|
||||
* }
|
||||
* // Use data safely
|
||||
*/
|
||||
export type Result<T, E = Error> = [T, null] | [null, E];
|
||||
|
||||
/**
|
||||
* Wraps a function in try-catch and returns Result tuple
|
||||
* Similar to Go's (value, error) pattern
|
||||
*
|
||||
* @param fn Function to execute
|
||||
* @returns Tuple of [result, null] or [null, error]
|
||||
*/
|
||||
export function tryCatch<T>(fn: () => T): Result<T> {
|
||||
try {
|
||||
const result = fn();
|
||||
return [result, null];
|
||||
} catch (error) {
|
||||
return [null, error instanceof Error ? error : new Error(String(error))];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async version of tryCatch for promises
|
||||
*
|
||||
* @param fn Async function to execute
|
||||
* @returns Promise resolving to tuple of [result, null] or [null, error]
|
||||
*/
|
||||
export async function tryCatchAsync<T>(fn: () => Promise<T>): Promise<Result<T>> {
|
||||
try {
|
||||
const result = await fn();
|
||||
return [result, null];
|
||||
} catch (error) {
|
||||
return [null, error instanceof Error ? error : new Error(String(error))];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely executes a void function, ignoring any errors
|
||||
* Perfect for cleanup operations like unsubscribe where errors don't matter
|
||||
*
|
||||
* @param fn Function to execute safely
|
||||
*/
|
||||
export function safely(fn: () => void): void {
|
||||
try {
|
||||
fn();
|
||||
} catch {
|
||||
/* Ignore errors during cleanup */
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,73 @@
|
||||
/**
|
||||
* Utility Functions
|
||||
*
|
||||
* Common helper functions for the application including:
|
||||
* - UI styling utilities (shadcn)
|
||||
* - Date formatting for notices
|
||||
* - PocketBase file URL resolution
|
||||
*/
|
||||
|
||||
/* Shadcn */
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import type { RecordModel } from "pocketbase";
|
||||
import { pb } from "./pocketbase";
|
||||
|
||||
/* Combines Tailwind classes with proper conflict resolution */
|
||||
const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
|
||||
export { cn };
|
||||
|
||||
/**
|
||||
* Formats PocketBase created timestamp to human-readable Bosnian format
|
||||
* Examples: "Danas, 9:30", "Juče, 15:15", "18. Okt, 10:45"
|
||||
*
|
||||
* @param dateStr ISO 8601 datetime string from PocketBase
|
||||
* @returns Formatted date string in Bosnian
|
||||
*/
|
||||
export function formatPostedDate(dateStr: string): string {
|
||||
const date = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
||||
|
||||
const timeStr = date.toLocaleTimeString("bs-BA", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
|
||||
if (diffDays === 0) {
|
||||
return `Danas, ${timeStr}`;
|
||||
}
|
||||
|
||||
if (diffDays === 1) {
|
||||
return `Juče, ${timeStr}`;
|
||||
}
|
||||
|
||||
const dateStr2 = date.toLocaleDateString("bs-BA", {
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
});
|
||||
|
||||
return `${dateStr2}, ${timeStr}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a notice has expired
|
||||
*
|
||||
* @param expiresAt ISO 8601 datetime string
|
||||
* @returns true if the expiry date has passed, false otherwise
|
||||
*/
|
||||
export function isExpired(expiresAt: string): boolean {
|
||||
return new Date() > new Date(expiresAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PocketBase file URL for a record's file field
|
||||
*
|
||||
* @param record PocketBase record containing the file
|
||||
* @param filename Name of the file field
|
||||
* @returns Full URL to the file, or empty string if no file
|
||||
*/
|
||||
export function getFileUrl(record: RecordModel, filename: string): string {
|
||||
return filename ? pb.files.getURL(record, filename) : "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user