lib/ was never committed despite every page importing from it - a fresh clone couldn't build. public/logo-encap.jpg was likewise missing even though app/login/page.tsx already references it. Also gitignore .idea/, which shouldn't be tracked. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hQcuUQFue2bFvrYw2UQhK
175 lines
4.5 KiB
TypeScript
175 lines
4.5 KiB
TypeScript
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:8080/api';
|
|
import { LoginResponse, User, Association } from "./models";
|
|
|
|
// Store token in localStorage (we'll improve this later)
|
|
export function setAuthToken(token: string) {
|
|
localStorage.setItem('auth_token', token);
|
|
}
|
|
|
|
export function getAuthToken(): string | null {
|
|
return localStorage.getItem('auth_token');
|
|
}
|
|
|
|
export function removeAuthToken() {
|
|
localStorage.removeItem('auth_token');
|
|
}
|
|
|
|
// Helper to add Authorization header
|
|
function getHeaders(): HeadersInit {
|
|
const headers: HeadersInit = {
|
|
'Content-Type': 'application/json',
|
|
};
|
|
|
|
const token = getAuthToken();
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
export async function login(email: string, password: string): Promise<LoginResponse> {
|
|
const response = await fetch(`${API_BASE_URL}/login`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email, password }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Invalid credentials');
|
|
}
|
|
|
|
const data: LoginResponse = await response.json();
|
|
setAuthToken(data.token); // Store token
|
|
return data;
|
|
}
|
|
|
|
export async function getAssociation(id: string): Promise<Association> {
|
|
const response = await fetch(`${API_BASE_URL}/associations/${id}`, {
|
|
headers: getHeaders(),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 401 || response.status === 403) {
|
|
removeAuthToken();
|
|
throw new Error('Unauthorized');
|
|
}
|
|
throw new Error('Failed to fetch association');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
export async function getAssociations(): Promise<Association[]> {
|
|
const response = await fetch(`${API_BASE_URL}/associations`, {
|
|
headers: getHeaders(),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 401 || response.status === 403) {
|
|
removeAuthToken();
|
|
throw new Error('Unauthorized');
|
|
}
|
|
throw new Error('Failed to fetch associations');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
export async function createAssociation(association: Omit<Association, 'id' | 'created' | 'translit'>): Promise<Association> {
|
|
const response = await fetch(`${API_BASE_URL}/associations`, {
|
|
method: 'POST',
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(association),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to create association');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
export async function updateAssociation(id: string, association: Association): Promise<Association> {
|
|
const response = await fetch(`${API_BASE_URL}/associations/${id}`, {
|
|
method: 'PUT',
|
|
headers: getHeaders(),
|
|
body: JSON.stringify(association),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 401 || response.status === 403) {
|
|
removeAuthToken();
|
|
throw new Error('Unauthorized');
|
|
}
|
|
throw new Error('Failed to update association');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
export async function downloadAttestato(id: string, associationName: string): Promise<void> {
|
|
const response = await fetch(`${API_BASE_URL}/associations/${id}/attestato`, {
|
|
headers: getHeaders(),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 401 || response.status === 403) {
|
|
removeAuthToken();
|
|
throw new Error('Unauthorized');
|
|
}
|
|
throw new Error('Failed to generate attestato');
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
const url = window.URL.createObjectURL(blob);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = `attestato-${associationName}.pdf`;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
link.remove();
|
|
window.URL.revokeObjectURL(url);
|
|
}
|
|
|
|
export async function deleteAssociation(id: string): Promise<void> {
|
|
const response = await fetch(`${API_BASE_URL}/associations/${id}`, {
|
|
method: 'DELETE',
|
|
headers: getHeaders(),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 401 || response.status === 403) {
|
|
removeAuthToken();
|
|
throw new Error('Unauthorized');
|
|
}
|
|
throw new Error('Failed to delete association');
|
|
}
|
|
}
|
|
|
|
export function logout() {
|
|
removeAuthToken();
|
|
}
|
|
|
|
export async function verifyToken(): Promise<boolean> {
|
|
try {
|
|
const token = getAuthToken();
|
|
|
|
if (!token) return false;
|
|
|
|
const response = await fetch(`${API_BASE_URL}/associations`, {
|
|
headers: getHeaders(),
|
|
});
|
|
|
|
if (response.status === 401 || response.status === 403) {
|
|
removeAuthToken();
|
|
return false;
|
|
}
|
|
|
|
return response.ok;
|
|
|
|
} catch (error) {
|
|
console.error("verifyToken error:", error);
|
|
return false;
|
|
}
|
|
} |