diff --git a/frontend/src/lib/api/client.ts b/frontend/src/lib/api/client.ts index c3d18be..8d55409 100644 --- a/frontend/src/lib/api/client.ts +++ b/frontend/src/lib/api/client.ts @@ -151,6 +151,13 @@ export class ApiClient { }); } + async put(endpoint: string, data?: unknown): Promise { + return this.request(endpoint, { + method: "PUT", + body: data ? JSON.stringify(data) : undefined + }); + } + async delete(endpoint: string, data?: unknown): Promise { return this.request(endpoint, { method: "DELETE", diff --git a/frontend/src/lib/api/links.ts b/frontend/src/lib/api/links.ts index 124b51a..e34dc25 100644 --- a/frontend/src/lib/api/links.ts +++ b/frontend/src/lib/api/links.ts @@ -1,4 +1,5 @@ import { apiClient } from "./client"; +import { fetchUrlTitle } from "./metadata"; import type { Link, CreateLinkRequest, @@ -90,10 +91,7 @@ export const linksApi = { * @throws ApiError if link not found (404) or validation fails (400) */ async update(id: string, data: UpdateLinkRequest): Promise { - return apiClient.request(`/api/links/${id}`, { - method: "PUT", - body: JSON.stringify(data) - }); + return apiClient.put(`/api/links/${id}`, data); }, /** @@ -138,6 +136,15 @@ export const linksApi = { async importBatch(links: ImportLinkRow[]): Promise { return apiClient.post("/api/links/import", { links }); + }, + + /** + * Fetch the title of a destination URL + * @param url - The URL to fetch the title from + * @returns Page title or null if unable to fetch + */ + async fetchUrlTitle(url: string): Promise { + return fetchUrlTitle(url); } }; diff --git a/frontend/src/lib/utils/url-title.ts b/frontend/src/lib/api/metadata.ts similarity index 60% rename from frontend/src/lib/utils/url-title.ts rename to frontend/src/lib/api/metadata.ts index 8b9fb67..96eabdd 100644 --- a/frontend/src/lib/utils/url-title.ts +++ b/frontend/src/lib/api/metadata.ts @@ -1,4 +1,4 @@ -import { apiClient } from "$lib/api/client"; +import { apiClient } from "./client"; interface FetchTitleResponse { title: string | null; @@ -32,22 +32,3 @@ export async function fetchUrlTitle(url: string): Promise { return null; } } - -/** - * Debounce function to limit how often a function is called - * @param func - The function to debounce - * @param delay - Delay in milliseconds - * @returns Debounced function - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function debounce any>( - func: T, - delay: number -): (...args: Parameters) => void { - let timeoutId: ReturnType; - - return (...args: Parameters) => { - clearTimeout(timeoutId); - timeoutId = setTimeout(() => func(...args), delay); - }; -} diff --git a/frontend/src/lib/components/LinkModal.svelte b/frontend/src/lib/components/LinkModal.svelte index c22480d..5cb4f8f 100644 --- a/frontend/src/lib/components/LinkModal.svelte +++ b/frontend/src/lib/components/LinkModal.svelte @@ -8,7 +8,7 @@ UsageResponse, UtmParams } from "$lib/types/api"; - import { debounce, fetchUrlTitle } from "$lib/utils/url-title"; + import { debounce } from "$lib/utils/helpers"; import { createEventDispatcher, onMount } from "svelte"; interface Props { @@ -295,7 +295,7 @@ isFetchingTitle = true; try { - const fetchedTitle = await fetchUrlTitle(url.trim()); + const fetchedTitle = await linksApi.fetchUrlTitle(url.trim()); // Only set the title if user hasn't entered one and we got a valid title if (!hasUserEnteredTitle && fetchedTitle) { title = fetchedTitle; diff --git a/frontend/src/lib/stores/auth.ts b/frontend/src/lib/stores/auth.ts deleted file mode 100644 index a5ec67e..0000000 --- a/frontend/src/lib/stores/auth.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { writable } from "svelte/store"; -import type { User } from "$lib/types/api"; - -export const currentUser = writable(null); -export const isAuthenticated = writable(false); -export const isLoading = writable(true); diff --git a/frontend/src/lib/stores/links.ts b/frontend/src/lib/stores/links.ts deleted file mode 100644 index 4648fe0..0000000 --- a/frontend/src/lib/stores/links.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { writable } from "svelte/store"; -import type { Link } from "$lib/types/api"; - -export const links = writable([]); -export const linksLoading = writable(false); -export const currentPage = writable(1); -export const linksPerPage = writable(20); diff --git a/frontend/src/lib/utils/helpers.ts b/frontend/src/lib/utils/helpers.ts new file mode 100644 index 0000000..ac697cc --- /dev/null +++ b/frontend/src/lib/utils/helpers.ts @@ -0,0 +1,19 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any + +/** + * Debounce function to limit how often a function is called + * @param func - The function to debounce + * @param delay - Delay in milliseconds + * @returns Debounced function + */ +export function debounce any>( + func: T, + delay: number +): (...args: Parameters) => void { + let timeoutId: ReturnType; + + return (...args: Parameters) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => func(...args), delay); + }; +}