feat(api): refactored to api client to be per feature

This commit is contained in:
2026-02-06 20:41:07 -05:00
parent 789e55e79d
commit ac211f86b3
42 changed files with 1016 additions and 685 deletions

10
src/frontend/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

4
src/frontend/.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

8
src/frontend/.idea/frontend.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
src/frontend/.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/frontend.iml" filepath="$PROJECT_DIR$/.idea/frontend.iml" />
</modules>
</component>
</project>

6
src/frontend/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,14 @@
import { baseClient } from './base.js';
export const analyticsApi = {
getWorkspaceAnalytics(workspaceId, period = '7d', startDate = null, endDate = null) {
const params = new URLSearchParams();
if (startDate && endDate) {
params.set('startDate', startDate);
params.set('endDate', endDate);
} else {
params.set('period', period);
}
return baseClient.get(`/workspaces/${workspaceId}/analytics?${params.toString()}`);
},
};

View File

@@ -0,0 +1,15 @@
import { baseClient } from './base.js';
export const apiKeysApi = {
list(workspaceId) {
return baseClient.get(`/workspaces/${workspaceId}/api-keys`);
},
create(workspaceId, name, expiresAt = null, scopes = null) {
return baseClient.post(`/workspaces/${workspaceId}/api-keys`, { name, expiresAt, scopes });
},
delete(workspaceId, id) {
return baseClient.delete(`/workspaces/${workspaceId}/api-keys/${id}`);
},
};

View File

@@ -0,0 +1,15 @@
import { baseClient } from './base.js';
export const assetsApi = {
list(workspaceId) {
return baseClient.get(`/workspaces/${workspaceId}/assets`);
},
upload(workspaceId, file) {
return baseClient.upload(`/workspaces/${workspaceId}/assets`, file);
},
delete(workspaceId, id) {
return baseClient.delete(`/workspaces/${workspaceId}/assets/${id}`);
},
};

View File

@@ -0,0 +1,47 @@
import { baseClient } from './base.js';
export const authApi = {
register(email, password) {
return baseClient.post('/auth/register', { email, password });
},
login(email, password) {
return baseClient.post('/auth/login', { email, password });
},
forgotPassword(email) {
return baseClient.post('/auth/forgot', { email });
},
resetPassword(token, newPassword) {
return baseClient.post('/auth/reset', { token, newPassword });
},
getProfile() {
return baseClient.get('/auth/profile');
},
updateProfile(data) {
return baseClient.put('/auth/profile', data);
},
changePassword(currentPassword, newPassword) {
return baseClient.post('/auth/change-password', { currentPassword, newPassword });
},
resendVerification() {
return baseClient.post('/auth/resend-verification');
},
verifyEmail(token) {
return baseClient.post('/auth/verify-email', { token });
},
deleteAccount(password) {
return baseClient.delete('/auth/account', { password });
},
setToken(token) {
baseClient.setToken(token);
},
};

View File

@@ -0,0 +1,91 @@
const API_BASE = 'https://localhost:42001';
class BaseClient {
constructor() {
this.token = localStorage.getItem('token');
}
setToken(token) {
this.token = token;
if (token) {
localStorage.setItem('token', token);
} else {
localStorage.removeItem('token');
}
}
async #request(method, path, body = null) {
const headers = {};
if (this.token) {
headers['Authorization'] = `Bearer ${this.token}`;
}
const options = { method, headers };
if (body) {
headers['Content-Type'] = 'application/json';
options.body = JSON.stringify(body);
}
const response = await fetch(`${API_BASE}${path}`, options);
if (response.status === 401) {
this.setToken(null);
window.location.href = '/login';
throw new Error('Unauthorized');
}
const text = await response.text();
const data = text ? JSON.parse(text) : null;
if (!response.ok) {
throw new Error(data?.message || `HTTP ${response.status}`);
}
return data;
}
get(path) {
return this.#request('GET', path);
}
post(path, body = null) {
return this.#request('POST', path, body);
}
put(path, body) {
return this.#request('PUT', path, body);
}
delete(path, body = null) {
return this.#request('DELETE', path, body);
}
async upload(path, file) {
const headers = {};
if (this.token) {
headers['Authorization'] = `Bearer ${this.token}`;
}
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${API_BASE}${path}`, {
method: 'POST',
headers,
body: formData,
});
const data = await response.json();
if (!response.ok) {
throw new Error(data?.message || `HTTP ${response.status}`);
}
return data;
}
getBaseUrl() {
return API_BASE;
}
}
export const baseClient = new BaseClient();

View File

@@ -0,0 +1,20 @@
import { baseClient } from './base.js';
export const billingApi = {
createCheckoutSession(workspaceId, plan, successUrl, cancelUrl) {
return baseClient.post('/billing/checkout', {
workspaceId,
plan,
successUrl,
cancelUrl,
});
},
createPortalSession(returnUrl) {
return baseClient.post('/billing/portal', { returnUrl });
},
getSubscription(workspaceId) {
return baseClient.get(`/workspaces/${workspaceId}/subscription`);
},
};

View File

@@ -1,309 +0,0 @@
const API_BASE = 'https://localhost:42001';
class ApiClient {
constructor() {
this.token = localStorage.getItem('token');
}
setToken(token) {
this.token = token;
if (token) {
localStorage.setItem('token', token);
} else {
localStorage.removeItem('token');
}
}
async request(method, path, body = null) {
const headers = {
'Content-Type': 'application/json',
};
if (this.token) {
headers['Authorization'] = `Bearer ${this.token}`;
}
const options = { method, headers };
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(`${API_BASE}${path}`, options);
if (response.status === 401) {
this.setToken(null);
window.location.href = '/login';
throw new Error('Unauthorized');
}
const text = await response.text();
const data = text ? JSON.parse(text) : null;
if (!response.ok) {
throw new Error(data?.message || `HTTP ${response.status}`);
}
return data;
}
async upload(path, file) {
const headers = {};
if (this.token) {
headers['Authorization'] = `Bearer ${this.token}`;
}
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${API_BASE}${path}`, {
method: 'POST',
headers,
body: formData,
});
const data = await response.json();
if (!response.ok) {
throw new Error(data?.message || `HTTP ${response.status}`);
}
return data;
}
// Auth
register(email, password) {
return this.request('POST', '/auth/register', { email, password });
}
login(email, password) {
return this.request('POST', '/auth/login', { email, password });
}
forgotPassword(email) {
return this.request('POST', '/auth/forgot', { email });
}
resetPassword(token, newPassword) {
return this.request('POST', '/auth/reset', { token, newPassword });
}
getProfile() {
return this.request('GET', '/auth/profile');
}
updateProfile(data) {
return this.request('PUT', '/auth/profile', data);
}
changePassword(currentPassword, newPassword) {
return this.request('POST', '/auth/change-password', { currentPassword, newPassword });
}
resendVerification() {
return this.request('POST', '/auth/resend-verification');
}
verifyEmail(token) {
return this.request('POST', '/auth/verify-email', { token });
}
deleteAccount(password) {
return this.request('DELETE', '/auth/account', { password });
}
// Workspaces
listWorkspaces() {
return this.request('GET', '/workspaces');
}
createWorkspace(name) {
return this.request('POST', '/workspaces', { name });
}
getWorkspace(id) {
return this.request('GET', `/workspaces/${id}`);
}
updateWorkspace(id, name) {
return this.request('PUT', `/workspaces/${id}`, { name });
}
deleteWorkspace(id) {
return this.request('DELETE', `/workspaces/${id}`);
}
// Projects
listProjects(workspaceId) {
return this.request('GET', `/workspaces/${workspaceId}/projects`);
}
createProject(workspaceId, name, description = '') {
return this.request('POST', `/workspaces/${workspaceId}/projects`, { name, description });
}
getProject(workspaceId, id) {
return this.request('GET', `/workspaces/${workspaceId}/projects/${id}`);
}
updateProject(workspaceId, id, data) {
return this.request('PUT', `/workspaces/${workspaceId}/projects/${id}`, data);
}
deleteProject(workspaceId, id) {
return this.request('DELETE', `/workspaces/${workspaceId}/projects/${id}`);
}
// Links
listLinks(workspaceId, params = {}) {
const query = new URLSearchParams(params).toString();
const path = `/workspaces/${workspaceId}/links${query ? `?${query}` : ''}`;
return this.request('GET', path);
}
restoreLink(workspaceId, id) {
return this.request('POST', `/workspaces/${workspaceId}/links/${id}/restore`);
}
createLink(workspaceId, data) {
return this.request('POST', `/workspaces/${workspaceId}/links`, data);
}
getLink(workspaceId, id) {
return this.request('GET', `/workspaces/${workspaceId}/links/${id}`);
}
updateLink(workspaceId, id, data) {
return this.request('PUT', `/workspaces/${workspaceId}/links/${id}`, data);
}
deleteLink(workspaceId, id) {
return this.request('DELETE', `/workspaces/${workspaceId}/links/${id}`);
}
bulkCreateLinks(workspaceId, links) {
return this.request('POST', `/workspaces/${workspaceId}/links/bulk`, { links });
}
getLinkAnalytics(workspaceId, linkId, period = '7d', startDate = null, endDate = null) {
const params = new URLSearchParams();
if (startDate && endDate) {
params.set('startDate', startDate);
params.set('endDate', endDate);
} else {
params.set('period', period);
}
return this.request('GET', `/workspaces/${workspaceId}/links/${linkId}/analytics?${params.toString()}`);
}
// QR Codes
listQRCodes(workspaceId) {
return this.request('GET', `/workspaces/${workspaceId}/qrcodes`);
}
createQRCode(workspaceId, data) {
return this.request('POST', `/workspaces/${workspaceId}/qrcodes`, data);
}
getQRCode(workspaceId, id) {
return this.request('GET', `/workspaces/${workspaceId}/qrcodes/${id}`);
}
updateQRCode(workspaceId, id, data) {
return this.request('PUT', `/workspaces/${workspaceId}/qrcodes/${id}`, data);
}
deleteQRCode(workspaceId, id) {
return this.request('DELETE', `/workspaces/${workspaceId}/qrcodes/${id}`);
}
getQRCodePreview(workspaceId, id) {
return this.request('GET', `/workspaces/${workspaceId}/qrcodes/${id}/preview`);
}
getQRCodeExportUrl(workspaceId, id, format = 'png', size = 512) {
return `${API_BASE}/workspaces/${workspaceId}/qrcodes/${id}/export?format=${format}&size=${size}`;
}
getQRCodeAnalytics(workspaceId, qrCodeId, period = '7d') {
return this.request('GET', `/workspaces/${workspaceId}/qrcodes/${qrCodeId}/analytics?period=${period}`);
}
// Analytics
getWorkspaceAnalytics(workspaceId, period = '7d', startDate = null, endDate = null) {
const params = new URLSearchParams();
if (startDate && endDate) {
params.set('startDate', startDate);
params.set('endDate', endDate);
} else {
params.set('period', period);
}
return this.request('GET', `/workspaces/${workspaceId}/analytics?${params.toString()}`);
}
// Domains
listDomains(workspaceId) {
return this.request('GET', `/workspaces/${workspaceId}/domains`);
}
addDomain(workspaceId, hostname) {
return this.request('POST', `/workspaces/${workspaceId}/domains`, { hostname });
}
deleteDomain(workspaceId, id) {
return this.request('DELETE', `/workspaces/${workspaceId}/domains/${id}`);
}
verifyDomain(workspaceId, id) {
return this.request('POST', `/workspaces/${workspaceId}/domains/${id}/verify`, {});
}
// Assets
listAssets(workspaceId) {
return this.request('GET', `/workspaces/${workspaceId}/assets`);
}
uploadAsset(workspaceId, file) {
return this.upload(`/workspaces/${workspaceId}/assets`, file);
}
deleteAsset(workspaceId, id) {
return this.request('DELETE', `/workspaces/${workspaceId}/assets/${id}`);
}
// Billing
createCheckoutSession(workspaceId, plan, successUrl, cancelUrl) {
return this.request('POST', '/billing/checkout', {
workspaceId,
plan,
successUrl,
cancelUrl,
});
}
createPortalSession(returnUrl) {
return this.request('POST', '/billing/portal', { returnUrl });
}
getSubscription(workspaceId) {
return this.request('GET', `/workspaces/${workspaceId}/subscription`);
}
// Usage
getUsage(workspaceId = null) {
const path = workspaceId ? `/usage?workspaceId=${workspaceId}` : '/usage';
return this.request('GET', path);
}
// API Keys
listApiKeys(workspaceId) {
return this.request('GET', `/workspaces/${workspaceId}/api-keys`);
}
createApiKey(workspaceId, name, expiresAt = null, scopes = null) {
return this.request('POST', `/workspaces/${workspaceId}/api-keys`, { name, expiresAt, scopes });
}
deleteApiKey(workspaceId, id) {
return this.request('DELETE', `/workspaces/${workspaceId}/api-keys/${id}`);
}
}
export const api = new ApiClient();

View File

@@ -0,0 +1,19 @@
import { baseClient } from './base.js';
export const domainsApi = {
list(workspaceId) {
return baseClient.get(`/workspaces/${workspaceId}/domains`);
},
add(workspaceId, hostname) {
return baseClient.post(`/workspaces/${workspaceId}/domains`, { hostname });
},
delete(workspaceId, id) {
return baseClient.delete(`/workspaces/${workspaceId}/domains/${id}`);
},
verify(workspaceId, id) {
return baseClient.post(`/workspaces/${workspaceId}/domains/${id}/verify`);
},
};

View File

@@ -0,0 +1,12 @@
export { baseClient } from './base.js';
export { authApi } from './auth.js';
export { workspacesApi } from './workspaces.js';
export { projectsApi } from './projects.js';
export { linksApi } from './links.js';
export { qrcodesApi } from './qrcodes.js';
export { domainsApi } from './domains.js';
export { assetsApi } from './assets.js';
export { billingApi } from './billing.js';
export { analyticsApi } from './analytics.js';
export { apiKeysApi } from './apiKeys.js';
export { usageApi } from './usage.js';

View File

@@ -0,0 +1,44 @@
import { baseClient } from './base.js';
export const linksApi = {
list(workspaceId, params = {}) {
const query = new URLSearchParams(params).toString();
const path = `/workspaces/${workspaceId}/links${query ? `?${query}` : ''}`;
return baseClient.get(path);
},
create(workspaceId, data) {
return baseClient.post(`/workspaces/${workspaceId}/links`, data);
},
get(workspaceId, id) {
return baseClient.get(`/workspaces/${workspaceId}/links/${id}`);
},
update(workspaceId, id, data) {
return baseClient.put(`/workspaces/${workspaceId}/links/${id}`, data);
},
delete(workspaceId, id) {
return baseClient.delete(`/workspaces/${workspaceId}/links/${id}`);
},
restore(workspaceId, id) {
return baseClient.post(`/workspaces/${workspaceId}/links/${id}/restore`);
},
bulkCreate(workspaceId, links) {
return baseClient.post(`/workspaces/${workspaceId}/links/bulk`, { links });
},
getAnalytics(workspaceId, linkId, period = '7d', startDate = null, endDate = null) {
const params = new URLSearchParams();
if (startDate && endDate) {
params.set('startDate', startDate);
params.set('endDate', endDate);
} else {
params.set('period', period);
}
return baseClient.get(`/workspaces/${workspaceId}/links/${linkId}/analytics?${params.toString()}`);
},
};

View File

@@ -0,0 +1,23 @@
import { baseClient } from './base.js';
export const projectsApi = {
list(workspaceId) {
return baseClient.get(`/workspaces/${workspaceId}/projects`);
},
create(workspaceId, name, description = '') {
return baseClient.post(`/workspaces/${workspaceId}/projects`, { name, description });
},
get(workspaceId, id) {
return baseClient.get(`/workspaces/${workspaceId}/projects/${id}`);
},
update(workspaceId, id, data) {
return baseClient.put(`/workspaces/${workspaceId}/projects/${id}`, data);
},
delete(workspaceId, id) {
return baseClient.delete(`/workspaces/${workspaceId}/projects/${id}`);
},
};

View File

@@ -0,0 +1,35 @@
import { baseClient } from './base.js';
export const qrcodesApi = {
list(workspaceId) {
return baseClient.get(`/workspaces/${workspaceId}/qrcodes`);
},
create(workspaceId, data) {
return baseClient.post(`/workspaces/${workspaceId}/qrcodes`, data);
},
get(workspaceId, id) {
return baseClient.get(`/workspaces/${workspaceId}/qrcodes/${id}`);
},
update(workspaceId, id, data) {
return baseClient.put(`/workspaces/${workspaceId}/qrcodes/${id}`, data);
},
delete(workspaceId, id) {
return baseClient.delete(`/workspaces/${workspaceId}/qrcodes/${id}`);
},
getPreview(workspaceId, id) {
return baseClient.get(`/workspaces/${workspaceId}/qrcodes/${id}/preview`);
},
getExportUrl(workspaceId, id, format = 'png', size = 512) {
return `${baseClient.getBaseUrl()}/workspaces/${workspaceId}/qrcodes/${id}/export?format=${format}&size=${size}`;
},
getAnalytics(workspaceId, qrCodeId, period = '7d') {
return baseClient.get(`/workspaces/${workspaceId}/qrcodes/${qrCodeId}/analytics?period=${period}`);
},
};

View File

@@ -0,0 +1,8 @@
import { baseClient } from './base.js';
export const usageApi = {
get(workspaceId = null) {
const path = workspaceId ? `/usage?workspaceId=${workspaceId}` : '/usage';
return baseClient.get(path);
},
};

View File

@@ -0,0 +1,23 @@
import { baseClient } from './base.js';
export const workspacesApi = {
list() {
return baseClient.get('/workspaces');
},
create(name) {
return baseClient.post('/workspaces', { name });
},
get(id) {
return baseClient.get(`/workspaces/${id}`);
},
update(id, name) {
return baseClient.put(`/workspaces/${id}`, { name });
},
delete(id) {
return baseClient.delete(`/workspaces/${id}`);
},
};

View File

@@ -0,0 +1,36 @@
import { defineStore } from 'pinia';
import { analyticsApi } from '../api/analytics.js';
import { useWorkspaceStore } from './workspace.js';
export const useAnalyticsStore = defineStore('analytics', {
state: () => ({
analytics: null,
loading: false,
error: null,
}),
actions: {
async fetchAnalytics(period = '7d', startDate = null, endDate = null) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
this.loading = true;
try {
this.analytics = await analyticsApi.getWorkspaceAnalytics(
workspaceStore.currentWorkspaceId,
period,
startDate,
endDate
);
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
clearAll() {
this.analytics = null;
},
},
});

View File

@@ -0,0 +1,59 @@
import { defineStore } from 'pinia';
import { assetsApi } from '../api/assets.js';
import { useWorkspaceStore } from './workspace.js';
export const useAssetsStore = defineStore('assets', {
state: () => ({
assets: [],
loading: false,
error: null,
}),
actions: {
async fetchAssets() {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
this.loading = true;
try {
const response = await assetsApi.list(workspaceStore.currentWorkspaceId);
this.assets = response.assets || [];
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
async uploadAsset(file) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const asset = await assetsApi.upload(workspaceStore.currentWorkspaceId, file);
this.assets.unshift(asset);
return asset;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteAsset(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
await assetsApi.delete(workspaceStore.currentWorkspaceId, id);
this.assets = this.assets.filter(a => a.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
clearAll() {
this.assets = [];
},
},
});

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { api } from '../api/client'; import { authApi } from '../api/auth.js';
export const useAuthStore = defineStore('auth', { export const useAuthStore = defineStore('auth', {
state: () => ({ state: () => ({
@@ -19,12 +19,11 @@ export const useAuthStore = defineStore('auth', {
if (this.initialized) return; if (this.initialized) return;
if (this.token) { if (this.token) {
api.setToken(this.token); authApi.setToken(this.token);
try { try {
const profile = await api.getProfile(); const profile = await authApi.getProfile();
this.user = profile; this.user = profile;
} catch (err) { } catch (err) {
// Token is invalid, clear it
this.logout(); this.logout();
} }
} }
@@ -35,11 +34,11 @@ export const useAuthStore = defineStore('auth', {
this.loading = true; this.loading = true;
this.error = null; this.error = null;
try { try {
const response = await api.register(email, password); const response = await authApi.register(email, password);
this.token = response.token; this.token = response.token;
this.user = { email: response.email, isVerified: false }; this.user = { email: response.email, isVerified: false };
localStorage.setItem('token', response.token); localStorage.setItem('token', response.token);
api.setToken(response.token); authApi.setToken(response.token);
return true; return true;
} catch (err) { } catch (err) {
this.error = err.message; this.error = err.message;
@@ -53,11 +52,11 @@ export const useAuthStore = defineStore('auth', {
this.loading = true; this.loading = true;
this.error = null; this.error = null;
try { try {
const response = await api.login(email, password); const response = await authApi.login(email, password);
this.token = response.token; this.token = response.token;
this.user = { email: response.email }; this.user = { email: response.email };
localStorage.setItem('token', response.token); localStorage.setItem('token', response.token);
api.setToken(response.token); authApi.setToken(response.token);
return true; return true;
} catch (err) { } catch (err) {
this.error = err.message; this.error = err.message;
@@ -72,12 +71,12 @@ export const useAuthStore = defineStore('auth', {
this.user = null; this.user = null;
localStorage.removeItem('token'); localStorage.removeItem('token');
localStorage.removeItem('currentWorkspaceId'); localStorage.removeItem('currentWorkspaceId');
api.setToken(null); authApi.setToken(null);
}, },
async fetchProfile() { async fetchProfile() {
try { try {
this.user = await api.getProfile(); this.user = await authApi.getProfile();
} catch (err) { } catch (err) {
this.error = err.message; this.error = err.message;
} }

View File

@@ -0,0 +1,73 @@
import { defineStore } from 'pinia';
import { domainsApi } from '../api/domains.js';
import { useWorkspaceStore } from './workspace.js';
export const useDomainsStore = defineStore('domains', {
state: () => ({
domains: [],
loading: false,
error: null,
}),
actions: {
async fetchDomains() {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
this.loading = true;
try {
const response = await domainsApi.list(workspaceStore.currentWorkspaceId);
this.domains = response.domains || [];
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
async addDomain(hostname) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const domain = await domainsApi.add(workspaceStore.currentWorkspaceId, hostname);
this.domains.unshift(domain);
return domain;
} catch (err) {
this.error = err.message;
throw err;
}
},
async verifyDomain(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const result = await domainsApi.verify(workspaceStore.currentWorkspaceId, id);
await this.fetchDomains();
return result;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteDomain(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
await domainsApi.delete(workspaceStore.currentWorkspaceId, id);
this.domains = this.domains.filter(d => d.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
clearAll() {
this.domains = [];
},
},
});

View File

@@ -0,0 +1,8 @@
export { useAuthStore } from './auth.js';
export { useWorkspaceStore } from './workspace.js';
export { useProjectsStore } from './projects.js';
export { useLinksStore } from './links.js';
export { useQRCodesStore } from './qrcodes.js';
export { useDomainsStore } from './domains.js';
export { useAssetsStore } from './assets.js';
export { useAnalyticsStore } from './analytics.js';

View File

@@ -0,0 +1,119 @@
import { defineStore } from 'pinia';
import { linksApi } from '../api/links.js';
import { useWorkspaceStore } from './workspace.js';
export const useLinksStore = defineStore('links', {
state: () => ({
links: [],
loading: false,
error: null,
}),
actions: {
async fetchLinks(params = {}) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
this.loading = true;
try {
const response = await linksApi.list(workspaceStore.currentWorkspaceId, params);
this.links = response.links || [];
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
async createLink(data) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const link = await linksApi.create(workspaceStore.currentWorkspaceId, data);
this.links.unshift(link);
return link;
} catch (err) {
this.error = err.message;
throw err;
}
},
async updateLink(id, data) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const link = await linksApi.update(workspaceStore.currentWorkspaceId, id, data);
const index = this.links.findIndex(l => l.id === id);
if (index !== -1) {
this.links[index] = link;
}
return link;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteLink(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
await linksApi.delete(workspaceStore.currentWorkspaceId, id);
this.links = this.links.filter(l => l.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
async restoreLink(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const link = await linksApi.restore(workspaceStore.currentWorkspaceId, id);
const index = this.links.findIndex(l => l.id === id);
if (index !== -1) {
this.links[index] = link;
}
return link;
} catch (err) {
this.error = err.message;
throw err;
}
},
async bulkCreateLinks(links) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const result = await linksApi.bulkCreate(workspaceStore.currentWorkspaceId, links);
await this.fetchLinks();
return result;
} catch (err) {
this.error = err.message;
throw err;
}
},
async getLinkAnalytics(linkId, period = '7d', startDate = null, endDate = null) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
return await linksApi.getAnalytics(workspaceStore.currentWorkspaceId, linkId, period, startDate, endDate);
} catch (err) {
this.error = err.message;
throw err;
}
},
clearAll() {
this.links = [];
},
},
});

View File

@@ -0,0 +1,76 @@
import { defineStore } from 'pinia';
import { projectsApi } from '../api/projects.js';
import { useWorkspaceStore } from './workspace.js';
export const useProjectsStore = defineStore('projects', {
state: () => ({
projects: [],
loading: false,
error: null,
}),
actions: {
async fetchProjects() {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
this.loading = true;
try {
const response = await projectsApi.list(workspaceStore.currentWorkspaceId);
this.projects = response.projects || [];
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
async createProject(name, description = '') {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const project = await projectsApi.create(workspaceStore.currentWorkspaceId, name, description);
this.projects.unshift(project);
return project;
} catch (err) {
this.error = err.message;
throw err;
}
},
async updateProject(id, data) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const project = await projectsApi.update(workspaceStore.currentWorkspaceId, id, data);
const index = this.projects.findIndex(p => p.id === id);
if (index !== -1) {
this.projects[index] = project;
}
return project;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteProject(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
await projectsApi.delete(workspaceStore.currentWorkspaceId, id);
this.projects = this.projects.filter(p => p.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
clearAll() {
this.projects = [];
},
},
});

View File

@@ -0,0 +1,107 @@
import { defineStore } from 'pinia';
import { qrcodesApi } from '../api/qrcodes.js';
import { useWorkspaceStore } from './workspace.js';
export const useQRCodesStore = defineStore('qrcodes', {
state: () => ({
qrcodes: [],
loading: false,
error: null,
}),
actions: {
async fetchQRCodes() {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
this.loading = true;
try {
const response = await qrcodesApi.list(workspaceStore.currentWorkspaceId);
this.qrcodes = response.qrCodes || [];
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
},
async createQRCode(data) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const qrcode = await qrcodesApi.create(workspaceStore.currentWorkspaceId, data);
this.qrcodes.unshift(qrcode);
return qrcode;
} catch (err) {
this.error = err.message;
throw err;
}
},
async updateQRCode(id, data) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
const qrcode = await qrcodesApi.update(workspaceStore.currentWorkspaceId, id, data);
const index = this.qrcodes.findIndex(q => q.id === id);
if (index !== -1) {
this.qrcodes[index] = qrcode;
}
return qrcode;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteQRCode(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
await qrcodesApi.delete(workspaceStore.currentWorkspaceId, id);
this.qrcodes = this.qrcodes.filter(q => q.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
async getQRCodePreview(id) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
return await qrcodesApi.getPreview(workspaceStore.currentWorkspaceId, id);
} catch (err) {
this.error = err.message;
throw err;
}
},
getQRCodeExportUrl(id, format = 'png', size = 512) {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return null;
return qrcodesApi.getExportUrl(workspaceStore.currentWorkspaceId, id, format, size);
},
async getQRCodeAnalytics(qrCodeId, period = '7d') {
const workspaceStore = useWorkspaceStore();
if (!workspaceStore.currentWorkspaceId) return;
try {
return await qrcodesApi.getAnalytics(workspaceStore.currentWorkspaceId, qrCodeId, period);
} catch (err) {
this.error = err.message;
throw err;
}
},
clearAll() {
this.qrcodes = [];
},
},
});

View File

@@ -1,16 +1,10 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { api } from '../api/client'; import { workspacesApi } from '../api/workspaces.js';
export const useWorkspaceStore = defineStore('workspace', { export const useWorkspaceStore = defineStore('workspace', {
state: () => ({ state: () => ({
workspaces: [], workspaces: [],
currentWorkspace: null, currentWorkspace: null,
projects: [],
links: [],
qrcodes: [],
domains: [],
assets: [],
analytics: null,
loading: false, loading: false,
error: null, error: null,
initialized: false, initialized: false,
@@ -35,10 +29,9 @@ export const useWorkspaceStore = defineStore('workspace', {
async fetchWorkspaces() { async fetchWorkspaces() {
this.loading = true; this.loading = true;
try { try {
const response = await api.listWorkspaces(); const response = await workspacesApi.list();
this.workspaces = response.workspaces || []; this.workspaces = response.workspaces || [];
// Restore saved workspace or use first one
const savedId = localStorage.getItem('currentWorkspaceId'); const savedId = localStorage.getItem('currentWorkspaceId');
const saved = savedId ? this.workspaces.find(w => w.id === savedId) : null; const saved = savedId ? this.workspaces.find(w => w.id === savedId) : null;
@@ -61,18 +54,11 @@ export const useWorkspaceStore = defineStore('workspace', {
} else { } else {
localStorage.removeItem('currentWorkspaceId'); localStorage.removeItem('currentWorkspaceId');
} }
// Clear workspace-specific data
this.projects = [];
this.links = [];
this.qrcodes = [];
this.domains = [];
this.assets = [];
this.analytics = null;
}, },
async createWorkspace(name) { async createWorkspace(name) {
try { try {
const workspace = await api.createWorkspace(name); const workspace = await workspacesApi.create(name);
this.workspaces.push(workspace); this.workspaces.push(workspace);
return workspace; return workspace;
} catch (err) { } catch (err) {
@@ -83,7 +69,7 @@ export const useWorkspaceStore = defineStore('workspace', {
async updateWorkspace(id, name) { async updateWorkspace(id, name) {
try { try {
const updated = await api.updateWorkspace(id, name); const updated = await workspacesApi.update(id, name);
const index = this.workspaces.findIndex(w => w.id === id); const index = this.workspaces.findIndex(w => w.id === id);
if (index !== -1) { if (index !== -1) {
this.workspaces[index] = updated; this.workspaces[index] = updated;
@@ -100,7 +86,7 @@ export const useWorkspaceStore = defineStore('workspace', {
async deleteWorkspace(id) { async deleteWorkspace(id) {
try { try {
await api.deleteWorkspace(id); await workspacesApi.delete(id);
this.workspaces = this.workspaces.filter(w => w.id !== id); this.workspaces = this.workspaces.filter(w => w.id !== id);
if (this.currentWorkspace?.id === id) { if (this.currentWorkspace?.id === id) {
this.setCurrentWorkspace(this.workspaces[0] || null); this.setCurrentWorkspace(this.workspaces[0] || null);
@@ -111,254 +97,9 @@ export const useWorkspaceStore = defineStore('workspace', {
} }
}, },
// Projects
async fetchProjects() {
if (!this.currentWorkspaceId) return;
try {
const response = await api.listProjects(this.currentWorkspaceId);
this.projects = response.projects || [];
} catch (err) {
this.error = err.message;
}
},
async createProject(name, description = '') {
if (!this.currentWorkspaceId) return;
try {
const project = await api.createProject(this.currentWorkspaceId, name, description);
this.projects.unshift(project);
return project;
} catch (err) {
this.error = err.message;
throw err;
}
},
async updateProject(id, data) {
if (!this.currentWorkspaceId) return;
try {
const project = await api.updateProject(this.currentWorkspaceId, id, data);
const index = this.projects.findIndex(p => p.id === id);
if (index !== -1) {
this.projects[index] = project;
}
return project;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteProject(id) {
if (!this.currentWorkspaceId) return;
try {
await api.deleteProject(this.currentWorkspaceId, id);
this.projects = this.projects.filter(p => p.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
// Links
async fetchLinks(params = {}) {
if (!this.currentWorkspaceId) return;
try {
const response = await api.listLinks(this.currentWorkspaceId, params);
this.links = response.links || [];
} catch (err) {
this.error = err.message;
}
},
async createLink(data) {
if (!this.currentWorkspaceId) return;
try {
const link = await api.createLink(this.currentWorkspaceId, data);
this.links.unshift(link);
return link;
} catch (err) {
this.error = err.message;
throw err;
}
},
async updateLink(id, data) {
if (!this.currentWorkspaceId) return;
try {
const link = await api.updateLink(this.currentWorkspaceId, id, data);
const index = this.links.findIndex(l => l.id === id);
if (index !== -1) {
this.links[index] = link;
}
return link;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteLink(id) {
if (!this.currentWorkspaceId) return;
try {
await api.deleteLink(this.currentWorkspaceId, id);
this.links = this.links.filter(l => l.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
// QR Codes
async fetchQRCodes() {
if (!this.currentWorkspaceId) return;
try {
const response = await api.listQRCodes(this.currentWorkspaceId);
this.qrcodes = response.qrCodes || [];
} catch (err) {
this.error = err.message;
}
},
async createQRCode(data) {
if (!this.currentWorkspaceId) return;
try {
const qrcode = await api.createQRCode(this.currentWorkspaceId, data);
this.qrcodes.unshift(qrcode);
return qrcode;
} catch (err) {
this.error = err.message;
throw err;
}
},
async updateQRCode(id, data) {
if (!this.currentWorkspaceId) return;
try {
const qrcode = await api.updateQRCode(this.currentWorkspaceId, id, data);
const index = this.qrcodes.findIndex(q => q.id === id);
if (index !== -1) {
this.qrcodes[index] = qrcode;
}
return qrcode;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteQRCode(id) {
if (!this.currentWorkspaceId) return;
try {
await api.deleteQRCode(this.currentWorkspaceId, id);
this.qrcodes = this.qrcodes.filter(q => q.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
// Domains
async fetchDomains() {
if (!this.currentWorkspaceId) return;
try {
const response = await api.listDomains(this.currentWorkspaceId);
this.domains = response.domains || [];
} catch (err) {
this.error = err.message;
}
},
async addDomain(hostname) {
if (!this.currentWorkspaceId) return;
try {
const domain = await api.addDomain(this.currentWorkspaceId, hostname);
this.domains.unshift(domain);
return domain;
} catch (err) {
this.error = err.message;
throw err;
}
},
async verifyDomain(id) {
if (!this.currentWorkspaceId) return;
try {
const result = await api.verifyDomain(this.currentWorkspaceId, id);
// Refresh domains to get updated status
await this.fetchDomains();
return result;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteDomain(id) {
if (!this.currentWorkspaceId) return;
try {
await api.deleteDomain(this.currentWorkspaceId, id);
this.domains = this.domains.filter(d => d.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
// Assets
async fetchAssets() {
if (!this.currentWorkspaceId) return;
try {
const response = await api.listAssets(this.currentWorkspaceId);
this.assets = response.assets || [];
} catch (err) {
this.error = err.message;
}
},
async uploadAsset(file) {
if (!this.currentWorkspaceId) return;
try {
const asset = await api.uploadAsset(this.currentWorkspaceId, file);
this.assets.unshift(asset);
return asset;
} catch (err) {
this.error = err.message;
throw err;
}
},
async deleteAsset(id) {
if (!this.currentWorkspaceId) return;
try {
await api.deleteAsset(this.currentWorkspaceId, id);
this.assets = this.assets.filter(a => a.id !== id);
} catch (err) {
this.error = err.message;
throw err;
}
},
// Analytics
async fetchAnalytics(period = '7d', startDate = null, endDate = null) {
if (!this.currentWorkspaceId) return;
try {
this.analytics = await api.getWorkspaceAnalytics(this.currentWorkspaceId, period, startDate, endDate);
} catch (err) {
this.error = err.message;
}
},
// Clear all data (for logout)
clearAll() { clearAll() {
this.workspaces = []; this.workspaces = [];
this.currentWorkspace = null; this.currentWorkspace = null;
this.projects = [];
this.links = [];
this.qrcodes = [];
this.domains = [];
this.assets = [];
this.analytics = null;
this.initialized = false; this.initialized = false;
localStorage.removeItem('currentWorkspaceId'); localStorage.removeItem('currentWorkspaceId');
}, },

View File

@@ -200,9 +200,11 @@
<script setup> <script setup>
import { ref, computed, onMounted, watch } from 'vue'; import { ref, computed, onMounted, watch } from 'vue';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { useAnalyticsStore } from '../../stores/analytics.js';
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const analyticsStore = useAnalyticsStore();
const periods = [ const periods = [
{ label: '24h', value: '24h' }, { label: '24h', value: '24h' },
@@ -214,12 +216,12 @@ const period = ref('7d');
const isCustomRange = ref(false); const isCustomRange = ref(false);
const startDate = ref(''); const startDate = ref('');
const endDate = ref(''); const endDate = ref('');
const analytics = computed(() => workspaceStore.analytics); const analytics = computed(() => analyticsStore.analytics);
const setPeriod = async (p) => { const setPeriod = async (p) => {
isCustomRange.value = false; isCustomRange.value = false;
period.value = p; period.value = p;
await workspaceStore.fetchAnalytics(p); await analyticsStore.fetchAnalytics(p);
}; };
const toggleCustomRange = () => { const toggleCustomRange = () => {
@@ -235,7 +237,7 @@ const toggleCustomRange = () => {
const applyCustomRange = async () => { const applyCustomRange = async () => {
if (startDate.value && endDate.value) { if (startDate.value && endDate.value) {
await workspaceStore.fetchAnalytics(null, startDate.value, endDate.value); await analyticsStore.fetchAnalytics(null, startDate.value, endDate.value);
} }
}; };
@@ -267,12 +269,12 @@ const getReferrerPercentage = (value) => {
}; };
onMounted(async () => { onMounted(async () => {
await workspaceStore.fetchAnalytics(period.value); await analyticsStore.fetchAnalytics(period.value);
}); });
watch(() => workspaceStore.currentWorkspaceId, async () => { watch(() => workspaceStore.currentWorkspaceId, async () => {
if (workspaceStore.currentWorkspaceId) { if (workspaceStore.currentWorkspaceId) {
await workspaceStore.fetchAnalytics(period.value); await analyticsStore.fetchAnalytics(period.value);
} }
}); });
</script> </script>

View File

@@ -53,7 +53,7 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { api } from '../../api/client'; import { authApi } from '../../api/auth.js';
const email = ref(''); const email = ref('');
const loading = ref(false); const loading = ref(false);
@@ -65,7 +65,7 @@ const handleSubmit = async () => {
error.value = ''; error.value = '';
try { try {
await api.forgotPassword(email.value); await authApi.forgotPassword(email.value);
submitted.value = true; submitted.value = true;
} catch (err) { } catch (err) {
error.value = err.message; error.value = err.message;

View File

@@ -61,7 +61,7 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { api } from '../../api/client'; import { authApi } from '../../api/auth.js';
const route = useRoute(); const route = useRoute();
@@ -92,7 +92,7 @@ const handleSubmit = async () => {
error.value = ''; error.value = '';
try { try {
await api.resetPassword(token.value, password.value); await authApi.resetPassword(token.value, password.value);
success.value = true; success.value = true;
} catch (err) { } catch (err) {
error.value = err.message; error.value = err.message;

View File

@@ -69,7 +69,7 @@
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { api } from '../../api/client'; import { authApi } from '../../api/auth.js';
const route = useRoute(); const route = useRoute();
@@ -100,7 +100,7 @@ onMounted(async () => {
loading.value = true; loading.value = true;
try { try {
await api.verifyEmail(token.value); await authApi.verifyEmail(token.value);
success.value = true; success.value = true;
} catch (err) { } catch (err) {
error.value = err.message || 'Unable to verify your email. The link may be invalid or expired.'; error.value = err.message || 'Unable to verify your email. The link may be invalid or expired.';

View File

@@ -205,8 +205,9 @@
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { api } from '../../api/client'; import { billingApi } from '../../api/billing.js';
import { useWorkspaceStore } from '../../stores/workspace'; import { usageApi } from '../../api/usage.js';
import { useWorkspaceStore } from '../../stores/workspace.js';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
const route = useRoute(); const route = useRoute();
@@ -239,8 +240,8 @@ async function loadData() {
const workspaceId = workspaceStore.currentWorkspace?.id; const workspaceId = workspaceStore.currentWorkspace?.id;
const [usageData, subData] = await Promise.all([ const [usageData, subData] = await Promise.all([
api.getUsage(workspaceId), usageApi.get(workspaceId),
workspaceId ? api.getSubscription(workspaceId) : Promise.resolve(null), workspaceId ? billingApi.getSubscription(workspaceId) : Promise.resolve(null),
]); ]);
usage.value = usageData; usage.value = usageData;
@@ -265,7 +266,7 @@ async function upgrade(plan) {
const successUrl = window.location.origin + '/billing'; const successUrl = window.location.origin + '/billing';
const cancelUrl = window.location.origin + '/billing'; const cancelUrl = window.location.origin + '/billing';
const { url } = await api.createCheckoutSession( const { url } = await billingApi.createCheckoutSession(
workspaceId, workspaceId,
plan, plan,
successUrl, successUrl,
@@ -285,7 +286,7 @@ async function openPortal() {
try { try {
const returnUrl = window.location.origin + '/billing'; const returnUrl = window.location.origin + '/billing';
const { url } = await api.createPortalSession(returnUrl); const { url } = await billingApi.createPortalSession(returnUrl);
window.location.href = url; window.location.href = url;
} catch (err) { } catch (err) {
error.value = err.message; error.value = err.message;

View File

@@ -57,7 +57,7 @@
</svg> </svg>
</div> </div>
<div class="stat-content"> <div class="stat-content">
<p class="stat-value">{{ workspaceStore.links.length }}</p> <p class="stat-value">{{ linksStore.links.length }}</p>
<p class="stat-label">Active Links</p> <p class="stat-label">Active Links</p>
</div> </div>
</div> </div>
@@ -173,9 +173,13 @@
<script setup> <script setup>
import { ref, computed, onMounted, watch } from 'vue'; import { ref, computed, onMounted, watch } from 'vue';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { useLinksStore } from '../../stores/links.js';
import { useAnalyticsStore } from '../../stores/analytics.js';
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const linksStore = useLinksStore();
const analyticsStore = useAnalyticsStore();
const periods = [ const periods = [
{ label: '24h', value: '24h' }, { label: '24h', value: '24h' },
@@ -184,11 +188,11 @@ const periods = [
]; ];
const period = ref('7d'); const period = ref('7d');
const analytics = computed(() => workspaceStore.analytics); const analytics = computed(() => analyticsStore.analytics);
const setPeriod = async (p) => { const setPeriod = async (p) => {
period.value = p; period.value = p;
await workspaceStore.fetchAnalytics(p); await analyticsStore.fetchAnalytics(p);
}; };
const maxEvents = computed(() => { const maxEvents = computed(() => {
@@ -256,14 +260,14 @@ const getCountryName = (countryCode) => {
}; };
onMounted(async () => { onMounted(async () => {
await workspaceStore.fetchLinks(); await linksStore.fetchLinks();
await workspaceStore.fetchAnalytics(period.value); await analyticsStore.fetchAnalytics(period.value);
}); });
watch(() => workspaceStore.currentWorkspaceId, async () => { watch(() => workspaceStore.currentWorkspaceId, async () => {
if (workspaceStore.currentWorkspaceId) { if (workspaceStore.currentWorkspaceId) {
await workspaceStore.fetchLinks(); await linksStore.fetchLinks();
await workspaceStore.fetchAnalytics(period.value); await analyticsStore.fetchAnalytics(period.value);
} }
}); });
</script> </script>

View File

@@ -206,12 +206,14 @@
<script setup> <script setup>
import { ref, computed, onMounted, watch } from 'vue'; import { ref, computed, onMounted, watch } from 'vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { useDomainsStore } from '../../stores/domains.js';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const domainsStore = useDomainsStore();
const domains = computed(() => workspaceStore.domains); const domains = computed(() => domainsStore.domains);
const loading = ref(true); const loading = ref(true);
const adding = ref(false); const adding = ref(false);
const verifying = ref(false); const verifying = ref(false);
@@ -242,7 +244,7 @@ watch(() => workspaceStore.currentWorkspaceId, async () => {
async function loadDomains() { async function loadDomains() {
loading.value = true; loading.value = true;
try { try {
await workspaceStore.fetchDomains(); await domainsStore.fetchDomains();
} catch (err) { } catch (err) {
error.value = err.message; error.value = err.message;
} finally { } finally {
@@ -254,7 +256,7 @@ async function addDomain() {
adding.value = true; adding.value = true;
error.value = ''; error.value = '';
try { try {
const domain = await workspaceStore.addDomain(newDomain.value); const domain = await domainsStore.addDomain(newDomain.value);
showAddModal.value = false; showAddModal.value = false;
newDomain.value = ''; newDomain.value = '';
// Show verification instructions for the new domain // Show verification instructions for the new domain
@@ -277,7 +279,7 @@ async function verifyDomain() {
verifying.value = true; verifying.value = true;
verifyError.value = ''; verifyError.value = '';
try { try {
await workspaceStore.verifyDomain(verifyingDomain.value.id); await domainsStore.verifyDomain(verifyingDomain.value.id);
showVerifyModal.value = false; showVerifyModal.value = false;
verifyingDomain.value = null; verifyingDomain.value = null;
} catch (err) { } catch (err) {
@@ -295,7 +297,7 @@ function confirmDelete(domain) {
async function deleteDomain() { async function deleteDomain() {
deleting.value = true; deleting.value = true;
try { try {
await workspaceStore.deleteDomain(domainToDelete.value.id); await domainsStore.deleteDomain(domainToDelete.value.id);
showDeleteModal.value = false; showDeleteModal.value = false;
domainToDelete.value = null; domainToDelete.value = null;
} catch (err) { } catch (err) {

View File

@@ -147,8 +147,8 @@
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { api } from '../../api/client'; import { linksApi } from '../../api/links.js';
const route = useRoute(); const route = useRoute();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
@@ -170,7 +170,7 @@ const fetchData = async () => {
if (!workspaceId) return; if (!workspaceId) return;
try { try {
link.value = await api.getLink(workspaceId, linkId); link.value = await linksApi.get(workspaceId, linkId);
await fetchAnalytics(); await fetchAnalytics();
} catch (err) { } catch (err) {
console.error('Failed to fetch link:', err); console.error('Failed to fetch link:', err);
@@ -184,7 +184,7 @@ const fetchAnalytics = async () => {
if (!workspaceId) return; if (!workspaceId) return;
try { try {
analytics.value = await api.getLinkAnalytics(workspaceId, linkId, period.value); analytics.value = await linksApi.getAnalytics(workspaceId, linkId, period.value);
} catch (err) { } catch (err) {
console.error('Failed to fetch analytics:', err); console.error('Failed to fetch analytics:', err);
} }

View File

@@ -37,9 +37,9 @@
</div> </div>
</header> </header>
<div class="links-list" v-if="workspaceStore.links.length"> <div class="links-list" v-if="linksStore.links.length">
<div <div
v-for="link in workspaceStore.links" v-for="link in linksStore.links"
:key="link.id" :key="link.id"
class="link-card" class="link-card"
> >
@@ -311,10 +311,12 @@
<script setup> <script setup>
import { ref, computed, onMounted, watch } from 'vue'; import { ref, computed, onMounted, watch } from 'vue';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { api } from '../../api/client'; import { useLinksStore } from '../../stores/links.js';
import { linksApi } from '../../api/links.js';
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const linksStore = useLinksStore();
const showCreateModal = ref(false); const showCreateModal = ref(false);
const showDeleteModal = ref(false); const showDeleteModal = ref(false);
@@ -431,12 +433,12 @@ const saveLink = async () => {
}; };
if (editingLink.value) { if (editingLink.value) {
await workspaceStore.updateLink(editingLink.value.id, data); await linksStore.updateLink(editingLink.value.id, data);
} else { } else {
if (formData.value.slug) { if (formData.value.slug) {
data.slug = formData.value.slug; data.slug = formData.value.slug;
} }
await workspaceStore.createLink(data); await linksStore.createLink(data);
} }
closeModal(); closeModal();
@@ -454,7 +456,7 @@ const confirmDelete = (link) => {
const deleteLink = async () => { const deleteLink = async () => {
if (deletingLink.value) { if (deletingLink.value) {
await workspaceStore.deleteLink(deletingLink.value.id); await linksStore.deleteLink(deletingLink.value.id);
showDeleteModal.value = false; showDeleteModal.value = false;
deletingLink.value = null; deletingLink.value = null;
} }
@@ -517,12 +519,8 @@ const importBulkLinks = async () => {
bulkError.value = ''; bulkError.value = '';
try { try {
const workspaceId = workspaceStore.currentWorkspaceId; const result = await linksStore.bulkCreateLinks(parsedBulkLinks.value);
const result = await api.bulkCreateLinks(workspaceId, parsedBulkLinks.value);
bulkResults.value = result; bulkResults.value = result;
// Refresh links list
await workspaceStore.fetchLinks();
} catch (err) { } catch (err) {
bulkError.value = err.message; bulkError.value = err.message;
} finally { } finally {
@@ -532,30 +530,30 @@ const importBulkLinks = async () => {
const toggleDeleted = async () => { const toggleDeleted = async () => {
showDeleted.value = true; showDeleted.value = true;
await workspaceStore.fetchLinks({ includeDeleted: true }); await linksStore.fetchLinks({ includeDeleted: true });
}; };
const restoreLink = async (link) => { const restoreLink = async (link) => {
try { try {
await api.restoreLink(workspaceStore.currentWorkspaceId, link.id); await linksStore.restoreLink(link.id);
await workspaceStore.fetchLinks({ includeDeleted: showDeleted.value }); await linksStore.fetchLinks({ includeDeleted: showDeleted.value });
} catch (err) { } catch (err) {
console.error('Failed to restore link:', err); console.error('Failed to restore link:', err);
} }
}; };
onMounted(async () => { onMounted(async () => {
await workspaceStore.fetchLinks(); await linksStore.fetchLinks();
}); });
watch(() => workspaceStore.currentWorkspaceId, async () => { watch(() => workspaceStore.currentWorkspaceId, async () => {
if (workspaceStore.currentWorkspaceId) { if (workspaceStore.currentWorkspaceId) {
await workspaceStore.fetchLinks({ includeDeleted: showDeleted.value }); await linksStore.fetchLinks({ includeDeleted: showDeleted.value });
} }
}); });
watch(showDeleted, async (value) => { watch(showDeleted, async (value) => {
await workspaceStore.fetchLinks({ includeDeleted: value }); await linksStore.fetchLinks({ includeDeleted: value });
}); });
</script> </script>

View File

@@ -142,12 +142,14 @@
<script setup> <script setup>
import { ref, computed, onMounted, watch } from 'vue'; import { ref, computed, onMounted, watch } from 'vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { useProjectsStore } from '../../stores/projects.js';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const projectsStore = useProjectsStore();
const projects = computed(() => workspaceStore.projects); const projects = computed(() => projectsStore.projects);
const loading = ref(true); const loading = ref(true);
const saving = ref(false); const saving = ref(false);
const deleting = ref(false); const deleting = ref(false);
@@ -174,7 +176,7 @@ watch(() => workspaceStore.currentWorkspaceId, async () => {
async function loadProjects() { async function loadProjects() {
loading.value = true; loading.value = true;
try { try {
await workspaceStore.fetchProjects(); await projectsStore.fetchProjects();
} catch (err) { } catch (err) {
error.value = err.message; error.value = err.message;
} finally { } finally {
@@ -186,7 +188,7 @@ async function createProject() {
saving.value = true; saving.value = true;
error.value = ''; error.value = '';
try { try {
await workspaceStore.createProject(form.value.name, form.value.description); await projectsStore.createProject(form.value.name, form.value.description);
closeModals(); closeModals();
} catch (err) { } catch (err) {
error.value = err.message; error.value = err.message;
@@ -205,7 +207,7 @@ async function updateProject() {
saving.value = true; saving.value = true;
error.value = ''; error.value = '';
try { try {
await workspaceStore.updateProject(editingProject.value.id, { await projectsStore.updateProject(editingProject.value.id, {
name: form.value.name, name: form.value.name,
description: form.value.description description: form.value.description
}); });
@@ -225,7 +227,7 @@ function confirmDelete(project) {
async function deleteProject() { async function deleteProject() {
deleting.value = true; deleting.value = true;
try { try {
await workspaceStore.deleteProject(projectToDelete.value.id); await projectsStore.deleteProject(projectToDelete.value.id);
showDeleteModal.value = false; showDeleteModal.value = false;
projectToDelete.value = null; projectToDelete.value = null;
} catch (err) { } catch (err) {

View File

@@ -76,7 +76,7 @@
<label for="linkId">Link *</label> <label for="linkId">Link *</label>
<select id="linkId" v-model="formData.linkId" required> <select id="linkId" v-model="formData.linkId" required>
<option value="">Select a link</option> <option value="">Select a link</option>
<option v-for="link in workspaceStore.links" :key="link.id" :value="link.id"> <option v-for="link in linksStore.links" :key="link.id" :value="link.id">
{{ link.title || link.slug }} ({{ link.slug }}) {{ link.title || link.slug }} ({{ link.slug }})
</option> </option>
</select> </select>
@@ -254,12 +254,18 @@
import { ref, watch, onMounted, computed } from 'vue'; import { ref, watch, onMounted, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { api } from '../../api/client'; import { useLinksStore } from '../../stores/links.js';
import { useAssetsStore } from '../../stores/assets.js';
import { useQRCodesStore } from '../../stores/qrcodes.js';
import { qrcodesApi } from '../../api/qrcodes.js';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const linksStore = useLinksStore();
const assetsStore = useAssetsStore();
const qrcodesStore = useQRCodesStore();
const isEditing = computed(() => !!route.params.id); const isEditing = computed(() => !!route.params.id);
const saving = ref(false); const saving = ref(false);
@@ -267,7 +273,7 @@ const error = ref('');
const previewUrl = ref(''); const previewUrl = ref('');
const previewTimeout = ref(null); const previewTimeout = ref(null);
const uploadingLogo = ref(false); const uploadingLogo = ref(false);
const assets = computed(() => workspaceStore.assets.filter(a => a.type === 'Logo')); const assets = computed(() => assetsStore.assets.filter(a => a.type === 'Logo'));
const formData = ref({ const formData = ref({
name: '', name: '',
@@ -296,7 +302,7 @@ const eyeShapes = [
]; ];
const getLogoUrl = (assetId) => { const getLogoUrl = (assetId) => {
const asset = workspaceStore.assets.find(a => a.id === assetId); const asset = assetsStore.assets.find(a => a.id === assetId);
return asset?.url || ''; return asset?.url || '';
}; };
@@ -306,7 +312,7 @@ const handleLogoUpload = async (event) => {
uploadingLogo.value = true; uploadingLogo.value = true;
try { try {
const asset = await workspaceStore.uploadAsset(file); const asset = await assetsStore.uploadAsset(file);
formData.value.logoAssetId = asset.id; formData.value.logoAssetId = asset.id;
// If adding a logo, suggest high error correction // If adding a logo, suggest high error correction
if (formData.value.style.errorCorrectionLevel !== 'H') { if (formData.value.style.errorCorrectionLevel !== 'H') {
@@ -373,7 +379,7 @@ const fetchPreview = async () => {
// For now, we'll just show a placeholder until saved // For now, we'll just show a placeholder until saved
if (isEditing.value) { if (isEditing.value) {
try { try {
const preview = await api.getQRCodePreview(workspaceStore.currentWorkspaceId, route.params.id); const preview = await qrcodesStore.getQRCodePreview(route.params.id);
previewUrl.value = preview.dataUrl; previewUrl.value = preview.dataUrl;
} catch (err) { } catch (err) {
console.error('Preview error:', err); console.error('Preview error:', err);
@@ -400,9 +406,9 @@ const save = async () => {
if (!formData.value.logoAssetId) { if (!formData.value.logoAssetId) {
data.removeLogo = true; data.removeLogo = true;
} }
await workspaceStore.updateQRCode(route.params.id, data); await qrcodesStore.updateQRCode(route.params.id, data);
} else { } else {
await workspaceStore.createQRCode(data); await qrcodesStore.createQRCode(data);
} }
router.push('/qrcodes'); router.push('/qrcodes');
@@ -416,7 +422,7 @@ const save = async () => {
const downloadQR = async (format) => { const downloadQR = async (format) => {
if (!isEditing.value) return; if (!isEditing.value) return;
const url = api.getQRCodeExportUrl(workspaceStore.currentWorkspaceId, route.params.id, format, 512); const url = qrcodesStore.getQRCodeExportUrl(route.params.id, format, 512);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
link.download = `${formData.value.name}.${format}`; link.download = `${formData.value.name}.${format}`;
@@ -427,7 +433,7 @@ const loadExisting = async () => {
if (!isEditing.value) return; if (!isEditing.value) return;
try { try {
const qr = await api.getQRCode(workspaceStore.currentWorkspaceId, route.params.id); const qr = await qrcodesApi.get(workspaceStore.currentWorkspaceId, route.params.id);
const defaultStyle = { const defaultStyle = {
foregroundColor: '#000000', foregroundColor: '#000000',
backgroundColor: '#ffffff', backgroundColor: '#ffffff',
@@ -460,8 +466,8 @@ watch(() => formData.value.logoAssetId, () => {
onMounted(async () => { onMounted(async () => {
await Promise.all([ await Promise.all([
workspaceStore.fetchLinks(), linksStore.fetchLinks(),
workspaceStore.fetchAssets(), assetsStore.fetchAssets(),
]); ]);
if (isEditing.value) { if (isEditing.value) {
await loadExisting(); await loadExisting();

View File

@@ -168,8 +168,8 @@
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { api } from '../../api/client'; import { qrcodesApi } from '../../api/qrcodes.js';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
const route = useRoute(); const route = useRoute();
@@ -208,8 +208,8 @@ async function loadData() {
if (!workspaceId) return; if (!workspaceId) return;
const [qrData, analyticsData] = await Promise.all([ const [qrData, analyticsData] = await Promise.all([
api.getQRCode(workspaceId, id.value), qrcodesApi.get(workspaceId, id.value),
api.getQRCodeAnalytics(workspaceId, id.value, period.value) qrcodesApi.getAnalytics(workspaceId, id.value, period.value)
]); ]);
qrCode.value = qrData; qrCode.value = qrData;
@@ -226,7 +226,7 @@ async function setPeriod(newPeriod) {
loading.value = true; loading.value = true;
try { try {
const workspaceId = workspaceStore.currentWorkspace?.id; const workspaceId = workspaceStore.currentWorkspace?.id;
analytics.value = await api.getQRCodeAnalytics(workspaceId, id.value, newPeriod); analytics.value = await qrcodesApi.getAnalytics(workspaceId, id.value, newPeriod);
} catch (err) { } catch (err) {
error.value = err.message; error.value = err.message;
} finally { } finally {

View File

@@ -15,9 +15,9 @@
</router-link> </router-link>
</header> </header>
<div class="qrcodes-grid" v-if="workspaceStore.qrcodes.length"> <div class="qrcodes-grid" v-if="qrcodesStore.qrcodes.length">
<div <div
v-for="qr in workspaceStore.qrcodes" v-for="qr in qrcodesStore.qrcodes"
:key="qr.id" :key="qr.id"
class="qr-card" class="qr-card"
> >
@@ -98,19 +98,23 @@
<script setup> <script setup>
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch } from 'vue';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
import { useWorkspaceStore } from '../../stores/workspace'; import { useWorkspaceStore } from '../../stores/workspace.js';
import { api } from '../../api/client'; import { useQRCodesStore } from '../../stores/qrcodes.js';
import { useLinksStore } from '../../stores/links.js';
import { qrcodesApi } from '../../api/qrcodes.js';
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const qrcodesStore = useQRCodesStore();
const linksStore = useLinksStore();
const previews = ref({}); const previews = ref({});
const showDeleteModal = ref(false); const showDeleteModal = ref(false);
const deletingQR = ref(null); const deletingQR = ref(null);
const fetchPreviews = async () => { const fetchPreviews = async () => {
for (const qr of workspaceStore.qrcodes) { for (const qr of qrcodesStore.qrcodes) {
try { try {
const preview = await api.getQRCodePreview(workspaceStore.currentWorkspaceId, qr.id); const preview = await qrcodesStore.getQRCodePreview(qr.id);
previews.value[qr.id] = preview.dataUrl; previews.value[qr.id] = preview.dataUrl;
} catch (err) { } catch (err) {
console.error('Failed to fetch preview:', err); console.error('Failed to fetch preview:', err);
@@ -119,12 +123,12 @@ const fetchPreviews = async () => {
}; };
const getLinkSlug = (linkId) => { const getLinkSlug = (linkId) => {
const link = workspaceStore.links.find(l => l.id === linkId); const link = linksStore.links.find(l => l.id === linkId);
return link ? `/${link.slug}` : 'No link'; return link ? `/${link.slug}` : 'No link';
}; };
const downloadQR = async (qr, format) => { const downloadQR = async (qr, format) => {
const url = api.getQRCodeExportUrl(workspaceStore.currentWorkspaceId, qr.id, format, 512); const url = qrcodesStore.getQRCodeExportUrl(qr.id, format, 512);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
link.download = `${qr.name}.${format}`; link.download = `${qr.name}.${format}`;
@@ -138,7 +142,7 @@ const confirmDelete = (qr) => {
const deleteQR = async () => { const deleteQR = async () => {
if (deletingQR.value) { if (deletingQR.value) {
await workspaceStore.deleteQRCode(deletingQR.value.id); await qrcodesStore.deleteQRCode(deletingQR.value.id);
delete previews.value[deletingQR.value.id]; delete previews.value[deletingQR.value.id];
showDeleteModal.value = false; showDeleteModal.value = false;
deletingQR.value = null; deletingQR.value = null;
@@ -146,16 +150,16 @@ const deleteQR = async () => {
}; };
onMounted(async () => { onMounted(async () => {
await workspaceStore.fetchQRCodes(); await qrcodesStore.fetchQRCodes();
await workspaceStore.fetchLinks(); await linksStore.fetchLinks();
await fetchPreviews(); await fetchPreviews();
}); });
watch(() => workspaceStore.currentWorkspaceId, async () => { watch(() => workspaceStore.currentWorkspaceId, async () => {
if (workspaceStore.currentWorkspaceId) { if (workspaceStore.currentWorkspaceId) {
previews.value = {}; previews.value = {};
await workspaceStore.fetchQRCodes(); await qrcodesStore.fetchQRCodes();
await workspaceStore.fetchLinks(); await linksStore.fetchLinks();
await fetchPreviews(); await fetchPreviews();
} }
}); });

View File

@@ -277,9 +277,10 @@
<script setup> <script setup>
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { api } from '../../api/client'; import { authApi } from '../../api/auth.js';
import { useAuthStore } from '../../stores/auth'; import { apiKeysApi } from '../../api/apiKeys.js';
import { useWorkspaceStore } from '../../stores/workspace'; import { useAuthStore } from '../../stores/auth.js';
import { useWorkspaceStore } from '../../stores/workspace.js';
import AppLayout from '../../components/layout/AppLayout.vue'; import AppLayout from '../../components/layout/AppLayout.vue';
const router = useRouter(); const router = useRouter();
@@ -323,7 +324,7 @@ const deleteKeyError = ref('');
onMounted(async () => { onMounted(async () => {
try { try {
const data = await api.getProfile(); const data = await authApi.getProfile();
profile.value = data; profile.value = data;
await loadApiKeys(); await loadApiKeys();
} catch (err) { } catch (err) {
@@ -345,7 +346,7 @@ async function loadApiKeys() {
loadingKeys.value = true; loadingKeys.value = true;
try { try {
const result = await api.listApiKeys(workspaceId); const result = await apiKeysApi.list(workspaceId);
apiKeys.value = result.apiKeys; apiKeys.value = result.apiKeys;
} catch (err) { } catch (err) {
console.error('Failed to load API keys:', err); console.error('Failed to load API keys:', err);
@@ -360,7 +361,7 @@ async function updateProfile() {
profileSuccess.value = ''; profileSuccess.value = '';
try { try {
const data = await api.updateProfile({ email: profile.value.email }); const data = await authApi.updateProfile({ email: profile.value.email });
profile.value = data; profile.value = data;
profileSuccess.value = 'Profile updated successfully'; profileSuccess.value = 'Profile updated successfully';
} catch (err) { } catch (err) {
@@ -381,7 +382,7 @@ async function changePassword() {
passwordSuccess.value = ''; passwordSuccess.value = '';
try { try {
await api.changePassword( await authApi.changePassword(
passwordForm.value.currentPassword, passwordForm.value.currentPassword,
passwordForm.value.newPassword passwordForm.value.newPassword
); );
@@ -400,7 +401,7 @@ async function resendVerification() {
resendingVerification.value = true; resendingVerification.value = true;
profileError.value = ''; profileError.value = '';
try { try {
await api.resendVerification(); await authApi.resendVerification();
profileSuccess.value = 'Verification email sent! Check your inbox.'; profileSuccess.value = 'Verification email sent! Check your inbox.';
} catch (err) { } catch (err) {
profileError.value = err.message; profileError.value = err.message;
@@ -414,7 +415,7 @@ async function deleteAccount() {
deleteError.value = ''; deleteError.value = '';
try { try {
await api.deleteAccount(deletePassword.value); await authApi.deleteAccount(deletePassword.value);
authStore.logout(); authStore.logout();
router.push('/'); router.push('/');
} catch (err) { } catch (err) {
@@ -440,7 +441,7 @@ async function createApiKey() {
expiresAt = date.toISOString(); expiresAt = date.toISOString();
} }
const result = await api.createApiKey(workspaceId, newKeyName.value, expiresAt); const result = await apiKeysApi.create(workspaceId, newKeyName.value, expiresAt);
newlyCreatedKey.value = result.key; newlyCreatedKey.value = result.key;
apiKeys.value.unshift({ apiKeys.value.unshift({
id: result.id, id: result.id,
@@ -487,7 +488,7 @@ async function deleteApiKey() {
deleteKeyError.value = ''; deleteKeyError.value = '';
try { try {
await api.deleteApiKey(workspaceId, keyToDelete.value.id); await apiKeysApi.delete(workspaceId, keyToDelete.value.id);
apiKeys.value = apiKeys.value.filter(k => k.id !== keyToDelete.value.id); apiKeys.value = apiKeys.value.filter(k => k.id !== keyToDelete.value.id);
keyToDelete.value = null; keyToDelete.value = null;
} catch (err) { } catch (err) {