2026-03-27 12:23:35 +01:00
|
|
|
const SUPABASE_URL = 'https://hwithddwaapyhnfwcesj.supabase.co';
|
2026-04-23 09:59:46 +02:00
|
|
|
const SUPABASE_ANON_KEY = 'sb_publishable_fGXkh0bSrAOqSk2jWKAzSg_NJD9YPCv';
|
|
|
|
|
|
|
|
|
|
function authHeaders(token?: string): Record<string, string> {
|
|
|
|
|
return {
|
|
|
|
|
'apikey': SUPABASE_ANON_KEY,
|
|
|
|
|
'Authorization': `Bearer ${token ?? SUPABASE_ANON_KEY}`,
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-03-27 12:23:35 +01:00
|
|
|
|
2026-04-10 09:47:35 +02:00
|
|
|
export interface ExcelRow extends Array<any> {}
|
|
|
|
|
|
|
|
|
|
export interface SyncedRow {
|
|
|
|
|
data: ExcelRow;
|
|
|
|
|
status?: 'pending' | 'synced';
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 12:03:12 +02:00
|
|
|
export async function getAllSyncedRows(token?: string): Promise<Record<string, SyncedRow>> {
|
2026-04-10 09:47:35 +02:00
|
|
|
try {
|
|
|
|
|
const response = await fetch(
|
|
|
|
|
`${SUPABASE_URL}/rest/v1/products?select=product_id,data,status&order=updated_at.desc`,
|
|
|
|
|
{
|
2026-04-10 19:45:05 +02:00
|
|
|
cache: 'no-store',
|
2026-04-23 09:59:46 +02:00
|
|
|
headers: authHeaders(token),
|
2026-04-10 09:47:35 +02:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-10 19:52:30 +02:00
|
|
|
if (!response.ok) {
|
2026-04-23 09:59:46 +02:00
|
|
|
const errText = await response.text();
|
|
|
|
|
console.error('getAllSyncedRows failed:', response.status, errText);
|
|
|
|
|
return {};
|
2026-04-10 19:52:30 +02:00
|
|
|
}
|
2026-04-10 09:47:35 +02:00
|
|
|
const rows = await response.json();
|
|
|
|
|
const result: Record<string, SyncedRow> = {};
|
|
|
|
|
for (const row of rows) {
|
|
|
|
|
result[row.product_id] = { data: row.data, status: row.status };
|
2026-04-09 08:22:58 +02:00
|
|
|
}
|
2026-04-10 09:47:35 +02:00
|
|
|
return result;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error fetching synced rows from Supabase:', error);
|
|
|
|
|
return {};
|
2026-04-09 08:22:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 12:06:16 +02:00
|
|
|
export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow, token?: string): Promise<{ success: boolean; error?: string }> {
|
2026-03-27 12:23:35 +01:00
|
|
|
try {
|
2026-04-10 09:47:35 +02:00
|
|
|
const response = await fetch(
|
2026-04-10 10:48:05 +02:00
|
|
|
`${SUPABASE_URL}/rest/v1/products`,
|
2026-04-10 09:47:35 +02:00
|
|
|
{
|
2026-04-10 10:48:05 +02:00
|
|
|
method: 'POST',
|
2026-04-10 09:47:35 +02:00
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
2026-04-23 09:59:46 +02:00
|
|
|
...authHeaders(token),
|
|
|
|
|
'Prefer': 'resolution=merge-duplicates',
|
2026-04-10 09:47:35 +02:00
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
product_id: articleNo,
|
|
|
|
|
data: rowData,
|
|
|
|
|
status: 'synced',
|
|
|
|
|
updated_at: new Date().toISOString()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
);
|
2026-03-27 12:23:35 +01:00
|
|
|
|
2026-04-10 12:06:16 +02:00
|
|
|
if (!response.ok) {
|
|
|
|
|
const err = await response.json().catch(() => ({}));
|
2026-04-23 09:59:46 +02:00
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: `${response.status} ${response.statusText}: ${err.message || err.error_description || 'Unknown error'}`
|
2026-04-10 12:06:16 +02:00
|
|
|
};
|
|
|
|
|
}
|
2026-04-23 09:59:46 +02:00
|
|
|
|
2026-04-10 12:06:16 +02:00
|
|
|
return { success: true };
|
|
|
|
|
} catch (error: any) {
|
2026-03-27 12:23:35 +01:00
|
|
|
console.error('Error saving to Supabase:', error);
|
2026-04-10 12:06:16 +02:00
|
|
|
return { success: false, error: error.message || 'Network error' };
|
2026-03-27 12:23:35 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-09 09:24:17 +02:00
|
|
|
export interface HistoryEntry {
|
2026-04-10 09:47:35 +02:00
|
|
|
id?: number;
|
2026-04-09 09:24:17 +02:00
|
|
|
product_id: string;
|
|
|
|
|
article_name: string;
|
|
|
|
|
old_data: ExcelRow;
|
|
|
|
|
new_data: ExcelRow;
|
|
|
|
|
changed_by: string;
|
2026-04-10 09:47:35 +02:00
|
|
|
changed_at: string;
|
2026-04-09 09:24:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function saveHistoryEntry(
|
2026-04-10 09:47:35 +02:00
|
|
|
productId: string,
|
2026-04-09 09:24:17 +02:00
|
|
|
articleName: string,
|
|
|
|
|
oldData: ExcelRow,
|
|
|
|
|
newData: ExcelRow,
|
2026-04-10 12:03:12 +02:00
|
|
|
changedBy: string,
|
|
|
|
|
token?: string
|
2026-04-10 12:06:16 +02:00
|
|
|
): Promise<{ success: boolean; error?: string }> {
|
2026-04-09 09:24:17 +02:00
|
|
|
try {
|
2026-04-10 09:47:35 +02:00
|
|
|
const response = await fetch(
|
|
|
|
|
`${SUPABASE_URL}/rest/v1/products_history`,
|
|
|
|
|
{
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
2026-04-23 09:59:46 +02:00
|
|
|
...authHeaders(token),
|
|
|
|
|
'Prefer': 'return=minimal',
|
2026-04-10 09:47:35 +02:00
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
product_id: productId,
|
|
|
|
|
article_name: articleName,
|
|
|
|
|
old_data: oldData,
|
|
|
|
|
new_data: newData,
|
|
|
|
|
changed_by: changedBy,
|
|
|
|
|
changed_at: new Date().toISOString()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
);
|
2026-04-09 09:24:17 +02:00
|
|
|
|
2026-04-10 12:06:16 +02:00
|
|
|
if (!response.ok) {
|
|
|
|
|
const err = await response.json().catch(() => ({}));
|
2026-04-23 09:59:46 +02:00
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: `History ${response.status}: ${err.message || 'Unknown error'}`
|
2026-04-10 12:06:16 +02:00
|
|
|
};
|
|
|
|
|
}
|
2026-04-23 09:59:46 +02:00
|
|
|
|
2026-04-10 12:06:16 +02:00
|
|
|
return { success: true };
|
|
|
|
|
} catch (error: any) {
|
2026-04-09 09:24:17 +02:00
|
|
|
console.error('Error saving history to Supabase:', error);
|
2026-04-10 12:06:16 +02:00
|
|
|
return { success: false, error: error.message || 'Network error' };
|
2026-04-09 09:24:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 12:03:12 +02:00
|
|
|
export async function getHistory(token?: string): Promise<HistoryEntry[]> {
|
2026-04-09 09:24:17 +02:00
|
|
|
try {
|
|
|
|
|
const response = await fetch(
|
|
|
|
|
`${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.desc&limit=100`,
|
|
|
|
|
{
|
2026-04-10 19:45:05 +02:00
|
|
|
cache: 'no-store',
|
2026-04-23 09:59:46 +02:00
|
|
|
headers: authHeaders(token),
|
2026-04-09 09:24:17 +02:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-10 19:48:33 +02:00
|
|
|
if (!response.ok) {
|
|
|
|
|
const err = await response.text();
|
|
|
|
|
return [{
|
2026-04-23 09:59:46 +02:00
|
|
|
id: -1,
|
2026-04-10 19:48:33 +02:00
|
|
|
product_id: 'ERROR',
|
|
|
|
|
article_name: `Failed: ${response.status} ${err}`,
|
|
|
|
|
old_data: [],
|
|
|
|
|
new_data: [],
|
|
|
|
|
changed_at: new Date().toISOString(),
|
|
|
|
|
changed_by: 'system'
|
|
|
|
|
}];
|
|
|
|
|
}
|
2026-04-09 09:24:17 +02:00
|
|
|
return await response.json();
|
2026-04-10 19:48:33 +02:00
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error('Error fetching history:', error);
|
|
|
|
|
return [{
|
2026-04-23 09:59:46 +02:00
|
|
|
id: -2,
|
|
|
|
|
product_id: 'EXCEPTION',
|
|
|
|
|
article_name: `Message: ${error.message}`,
|
|
|
|
|
old_data: [],
|
|
|
|
|
new_data: [],
|
|
|
|
|
changed_at: new Date().toISOString(),
|
|
|
|
|
changed_by: 'system'
|
2026-04-10 19:48:33 +02:00
|
|
|
}];
|
2026-04-09 09:24:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-04-10 09:47:35 +02:00
|
|
|
|
2026-04-10 12:03:12 +02:00
|
|
|
export async function deleteHistoryEntry(id: string, token?: string): Promise<boolean> {
|
2026-04-10 09:47:35 +02:00
|
|
|
try {
|
|
|
|
|
const response = await fetch(
|
|
|
|
|
`${SUPABASE_URL}/rest/v1/products_history?id=eq.${encodeURIComponent(id)}`,
|
|
|
|
|
{
|
|
|
|
|
method: 'DELETE',
|
2026-04-23 09:59:46 +02:00
|
|
|
headers: authHeaders(token),
|
2026-04-10 09:47:35 +02:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return response.ok;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error deleting history from Supabase:', error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 12:03:12 +02:00
|
|
|
export async function resetAllPendingRows(token?: string): Promise<boolean> {
|
2026-04-10 09:47:35 +02:00
|
|
|
try {
|
|
|
|
|
const response = await fetch(
|
|
|
|
|
`${SUPABASE_URL}/rest/v1/products?status=eq.pending`,
|
|
|
|
|
{
|
|
|
|
|
method: 'PATCH',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
2026-04-23 09:59:46 +02:00
|
|
|
...authHeaders(token),
|
|
|
|
|
'Prefer': 'return=minimal',
|
2026-04-10 09:47:35 +02:00
|
|
|
},
|
|
|
|
|
body: JSON.stringify({ status: 'synced' })
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return response.ok;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error resetting pending rows in Supabase:', error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-04-23 09:59:46 +02:00
|
|
|
}
|