Rename resources/scripts-v2 to resources/scripts and drop @v2 alias

Now that the legacy v1 frontend (commit 064bdf53) is gone, the v2 directory is the only frontend and the v2 suffix is just noise. Renames resources/scripts-v2 to resources/scripts via git mv (so git records the move as renames, preserving blame and log --follow), then bulk-rewrites the 152 files that imported via @v2/... to use @/scripts/... instead. The existing @ alias (resources/) covers the new path with no extra config needed.

Drops the now-unused @v2 alias from vite.config.js and points the laravel-vite-plugin entry at resources/scripts/main.ts. Updates the only blade reference (resources/views/app.blade.php) to match. The package.json test script (eslint ./resources/scripts) automatically targets the right place after the rename without any edit.

Verified: npm run build exits clean and the Vite warning lines now reference resources/scripts/plugins/i18n.ts, confirming every import resolved through the new path. git log --follow on any moved file walks back through its scripts-v2 history.
This commit is contained in:
Darko Gjorgjijoski
2026-04-07 12:50:16 +02:00
parent 064bdf5395
commit 71388ec6a5
448 changed files with 381 additions and 382 deletions

View File

@@ -0,0 +1,28 @@
import axios, { type AxiosInstance, type InternalAxiosRequestConfig } from 'axios'
const client: AxiosInstance = axios.create({
withCredentials: true,
headers: {
common: {
'X-Requested-With': 'XMLHttpRequest',
},
},
})
client.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const companyId = localStorage.getItem('selectedCompany')
const authToken = localStorage.getItem('auth.token')
const isAdminMode = localStorage.getItem('isAdminMode') === 'true'
if (authToken) {
config.headers.Authorization = authToken
}
if (companyId && !isAdminMode) {
config.headers.company = companyId
}
return config
})
export { client }

View File

@@ -0,0 +1,170 @@
export const API = {
// Authentication & Password Reset
LOGIN: '/login',
LOGOUT: '/auth/logout',
FORGOT_PASSWORD: '/api/v1/auth/password/email',
RESET_PASSWORD: '/api/v1/auth/reset/password',
AUTH_CHECK: '/api/v1/auth/check',
CSRF_COOKIE: '/sanctum/csrf-cookie',
REGISTER_WITH_INVITATION: '/api/v1/auth/register-with-invitation',
// Invitation Registration (public)
INVITATION_DETAILS: '/api/v1/invitations', // append /{token}/details
// Invitations (user-scoped)
INVITATIONS_PENDING: '/api/v1/invitations/pending',
INVITATIONS: '/api/v1/invitations', // append /{token}/accept or /{token}/decline
// Bootstrap & General
BOOTSTRAP: '/api/v1/bootstrap',
CONFIG: '/api/v1/config',
CURRENT_COMPANY: '/api/v1/current-company',
SEARCH: '/api/v1/search',
SEARCH_USERS: '/api/v1/search/user',
APP_VERSION: '/api/v1/app/version',
COUNTRIES: '/api/v1/countries',
// Dashboard
DASHBOARD: '/api/v1/dashboard',
// Customers
CUSTOMERS: '/api/v1/customers',
CUSTOMERS_DELETE: '/api/v1/customers/delete',
CUSTOMER_STATS: '/api/v1/customers', // append /{id}/stats
// Items & Units
ITEMS: '/api/v1/items',
ITEMS_DELETE: '/api/v1/items/delete',
UNITS: '/api/v1/units',
// Invoices
INVOICES: '/api/v1/invoices',
INVOICES_DELETE: '/api/v1/invoices/delete',
INVOICE_TEMPLATES: '/api/v1/invoices/templates',
// Recurring Invoices
RECURRING_INVOICES: '/api/v1/recurring-invoices',
RECURRING_INVOICES_DELETE: '/api/v1/recurring-invoices/delete',
RECURRING_INVOICE_FREQUENCY: '/api/v1/recurring-invoice-frequency',
// Estimates
ESTIMATES: '/api/v1/estimates',
ESTIMATES_DELETE: '/api/v1/estimates/delete',
ESTIMATE_TEMPLATES: '/api/v1/estimates/templates',
// Expenses
EXPENSES: '/api/v1/expenses',
EXPENSES_DELETE: '/api/v1/expenses/delete',
// Expense Categories
CATEGORIES: '/api/v1/categories',
// Payments
PAYMENTS: '/api/v1/payments',
PAYMENTS_DELETE: '/api/v1/payments/delete',
PAYMENT_METHODS: '/api/v1/payment-methods',
// Custom Fields
CUSTOM_FIELDS: '/api/v1/custom-fields',
// Notes
NOTES: '/api/v1/notes',
// Tax Types
TAX_TYPES: '/api/v1/tax-types',
// Roles & Abilities
ROLES: '/api/v1/roles',
ABILITIES: '/api/v1/abilities',
// Company
COMPANY: '/api/v1/company',
COMPANY_UPLOAD_LOGO: '/api/v1/company/upload-logo',
COMPANY_SETTINGS: '/api/v1/company/settings',
COMPANY_HAS_TRANSACTIONS: '/api/v1/company/has-transactions',
COMPANIES: '/api/v1/companies',
COMPANIES_DELETE: '/api/v1/companies/delete',
TRANSFER_OWNERSHIP: '/api/v1/transfer/ownership', // append /{userId}
// Company Invitations (company-scoped)
COMPANY_INVITATIONS: '/api/v1/company-invitations',
// Members
MEMBERS: '/api/v1/members',
MEMBERS_DELETE: '/api/v1/members/delete',
// User Profile & Settings
ME: '/api/v1/me',
ME_SETTINGS: '/api/v1/me/settings',
ME_UPLOAD_AVATAR: '/api/v1/me/upload-avatar',
// Global Settings (admin)
SETTINGS: '/api/v1/settings',
// Mail Configuration (global)
MAIL_DRIVERS: '/api/v1/mail/drivers',
MAIL_CONFIG: '/api/v1/mail/config',
MAIL_TEST: '/api/v1/mail/test',
// Company Mail Configuration
COMPANY_MAIL_DEFAULT_CONFIG: '/api/v1/company/mail/config',
COMPANY_MAIL_CONFIG: '/api/v1/company/mail/company-config',
COMPANY_MAIL_TEST: '/api/v1/company/mail/company-test',
// PDF Configuration
PDF_DRIVERS: '/api/v1/pdf/drivers',
PDF_CONFIG: '/api/v1/pdf/config',
// Disks & Backups
DISKS: '/api/v1/disks',
DISK_DRIVERS: '/api/v1/disk/drivers',
DISK_PURPOSES: '/api/v1/disk/purposes',
BACKUPS: '/api/v1/backups',
DOWNLOAD_BACKUP: '/api/v1/download-backup',
// Fonts
FONTS_STATUS: '/api/v1/fonts/status',
FONTS_INSTALL: '/api/v1/fonts',
// Exchange Rates & Currencies
CURRENCIES: '/api/v1/currencies',
CURRENCIES_USED: '/api/v1/currencies/used',
CURRENCIES_BULK_UPDATE: '/api/v1/currencies/bulk-update-exchange-rate',
EXCHANGE_RATE_PROVIDERS: '/api/v1/exchange-rate-providers',
USED_CURRENCIES: '/api/v1/used-currencies',
SUPPORTED_CURRENCIES: '/api/v1/supported-currencies',
// Serial Numbers
NEXT_NUMBER: '/api/v1/next-number',
NUMBER_PLACEHOLDERS: '/api/v1/number-placeholders',
// Formats
TIMEZONES: '/api/v1/timezones',
DATE_FORMATS: '/api/v1/date/formats',
TIME_FORMATS: '/api/v1/time/formats',
// Modules
MODULES: '/api/v1/modules',
MODULES_CHECK: '/api/v1/modules/check',
MODULES_DOWNLOAD: '/api/v1/modules/download',
MODULES_UPLOAD: '/api/v1/modules/upload',
MODULES_UNZIP: '/api/v1/modules/unzip',
MODULES_COPY: '/api/v1/modules/copy',
MODULES_COMPLETE: '/api/v1/modules/complete',
// Self Update
CHECK_UPDATE: '/api/v1/check/update',
UPDATE_DOWNLOAD: '/api/v1/update/download',
UPDATE_UNZIP: '/api/v1/update/unzip',
UPDATE_COPY: '/api/v1/update/copy',
UPDATE_DELETE: '/api/v1/update/delete',
UPDATE_CLEAN: '/api/v1/update/clean',
UPDATE_MIGRATE: '/api/v1/update/migrate',
UPDATE_FINISH: '/api/v1/update/finish',
// Super Admin
SUPER_ADMIN_DASHBOARD: '/api/v1/super-admin/dashboard',
SUPER_ADMIN_COMPANIES: '/api/v1/super-admin/companies',
SUPER_ADMIN_USERS: '/api/v1/super-admin/users',
SUPER_ADMIN_STOP_IMPERSONATING: '/api/v1/super-admin/stop-impersonating',
} as const

View File

@@ -0,0 +1,133 @@
export { client } from './client'
export { API } from './endpoints'
export {
authService,
bootstrapService,
invoiceService,
estimateService,
recurringInvoiceService,
customerService,
paymentService,
expenseService,
itemService,
companyService,
userService,
memberService,
settingService,
dashboardService,
reportService,
roleService,
taxTypeService,
customFieldService,
noteService,
exchangeRateService,
moduleService,
backupService,
mailService,
pdfService,
diskService,
updateService,
} from './services'
// Re-export all service types
export type {
LoginPayload,
LoginResponse,
ForgotPasswordPayload,
ResetPasswordPayload,
RegisterWithInvitationPayload,
BootstrapResponse,
MenuItem,
CurrentCompanyResponse,
InvoiceListParams,
InvoiceListResponse,
SendInvoicePayload,
InvoiceStatusPayload,
InvoiceTemplatesResponse,
EstimateListParams,
EstimateListResponse,
SendEstimatePayload,
EstimateStatusPayload,
EstimateTemplatesResponse,
RecurringInvoiceListParams,
RecurringInvoiceListResponse,
FrequencyDateParams,
FrequencyDateResponse,
CustomerListParams,
CustomerListResponse,
CustomerStatsChartData,
CustomerStatsParams,
CustomerStatsResponse,
PaymentListParams,
PaymentListResponse,
SendPaymentPayload,
CreatePaymentMethodPayload,
ExpenseListParams,
ExpenseListResponse,
CreateExpenseCategoryPayload,
ItemListParams,
ItemListResponse,
CreateItemPayload,
CreateUnitPayload,
UpdateCompanyPayload,
CompanySettingsPayload,
CreateCompanyPayload,
UpdateProfilePayload,
UserSettingsPayload,
MemberListParams,
MemberListResponse,
UpdateMemberPayload,
InviteMemberPayload,
DeleteMembersPayload,
ConfigResponse,
GlobalSettingsPayload,
DateFormat,
TimeFormat,
DashboardParams,
DashboardResponse,
ChartData,
ReportParams,
SalesReportResponse,
ProfitLossReportResponse,
ExpenseReportResponse,
TaxReportResponse,
CreateRolePayload,
AbilitiesResponse,
CreateTaxTypePayload,
CustomFieldListParams,
CreateCustomFieldPayload,
CreateNotePayload,
CreateExchangeRateProviderPayload,
BulkUpdatePayload,
ExchangeRateResponse,
ActiveProviderResponse,
Module,
ModuleInstallPayload,
ModuleCheckResponse,
Backup,
BackupListResponse,
CreateBackupPayload,
DeleteBackupParams,
MailConfig,
MailConfigResponse,
MailDriver,
SmtpConfig,
MailgunConfig,
SesConfig,
TestMailPayload,
PdfConfig,
PdfConfigResponse,
PdfDriver,
DomPdfConfig,
GotenbergConfig,
Disk,
DiskDriversResponse,
DiskDriverValue,
CreateDiskPayload,
CheckUpdateResponse,
UpdateRelease,
UpdateDownloadResponse,
UpdateStepResponse,
FinishUpdatePayload,
} from './services'

View File

@@ -0,0 +1,86 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { User } from '@/scripts/types/domain/user'
import type { ApiResponse } from '@/scripts/types/api'
export interface LoginPayload {
email: string
password: string
remember?: boolean
}
export interface LoginResponse {
token: string
user: User
}
export interface ForgotPasswordPayload {
email: string
}
export interface ResetPasswordPayload {
email: string
password: string
password_confirmation: string
token: string
}
export interface InvitationDetails {
email: string
company_name: string
invited_by: string
}
export interface RegisterWithInvitationPayload {
name: string
email: string
password: string
password_confirmation: string
invitation_token: string
}
export interface RegisterWithInvitationResponse {
type: string
token: string
}
export const authService = {
async refreshCsrfCookie(): Promise<void> {
await client.get(API.CSRF_COOKIE)
},
async login(payload: LoginPayload): Promise<ApiResponse<LoginResponse>> {
await client.get(API.CSRF_COOKIE)
const { data } = await client.post(API.LOGIN, payload)
return data
},
async logout(): Promise<void> {
await client.post(API.LOGOUT)
},
async forgotPassword(payload: ForgotPasswordPayload): Promise<ApiResponse<{ success: boolean }>> {
const { data } = await client.post(API.FORGOT_PASSWORD, payload)
return data
},
async resetPassword(payload: ResetPasswordPayload): Promise<ApiResponse<{ success: boolean }>> {
const { data } = await client.post(API.RESET_PASSWORD, payload)
return data
},
async check(): Promise<ApiResponse<{ success: boolean }>> {
const { data } = await client.get(API.AUTH_CHECK)
return data
},
async getInvitationDetails(token: string): Promise<ApiResponse<InvitationDetails>> {
const { data } = await client.get(`${API.INVITATION_DETAILS}/${token}/details`)
return data
},
async registerWithInvitation(payload: RegisterWithInvitationPayload): Promise<RegisterWithInvitationResponse> {
const { data } = await client.post<RegisterWithInvitationResponse>(API.REGISTER_WITH_INVITATION, payload)
return data
},
}

View File

@@ -0,0 +1,58 @@
import { client } from '../client'
import { API } from '../endpoints'
export interface Backup {
path: string
created_at: string
size: string
}
export interface BackupListResponse {
backups: Backup[]
disks: string[]
error?: string
error_message?: string
}
export interface CreateBackupPayload {
option: 'full' | 'only-db' | 'only-files'
file_disk_id: number
}
export interface DeleteBackupParams {
disk: string
path: string
file_disk_id?: number
}
export const backupService = {
async list(params: {
disk: string
file_disk_id?: number
}): Promise<BackupListResponse> {
const { data } = await client.get(API.BACKUPS, { params })
return data
},
async create(payload: CreateBackupPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.BACKUPS, payload)
return data
},
async delete(params: DeleteBackupParams): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.BACKUPS}/${params.disk}`, { params })
return data
},
async download(params: {
disk: string
path: string
file_disk_id?: number
}): Promise<Blob> {
const { data } = await client.get(API.DOWNLOAD_BACKUP, {
params,
responseType: 'blob',
})
return data
},
}

View File

@@ -0,0 +1,55 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { User, UserSetting } from '@/scripts/types/domain/user'
import type { Company } from '@/scripts/types/domain/company'
import type { Currency } from '@/scripts/types/domain/currency'
import type { Ability } from '@/scripts/types/domain/role'
export interface MenuItem {
title: string
name: string
link: string
icon: string
group: string
group_label?: string
ability?: string
}
export interface BootstrapResponse {
current_user: User
current_user_settings: Record<string, string>
current_user_abilities: Ability[]
companies: Company[]
current_company: Company | null
current_company_settings: Record<string, string>
current_company_currency: Currency | null
main_menu: MenuItem[]
setting_menu: MenuItem[]
config: Record<string, unknown>
global_settings: Record<string, string>
modules: string[]
admin_mode?: boolean
pending_invitations?: Array<{
token: string
company_name: string
invited_by: string
email: string
}>
}
export interface CurrentCompanyResponse {
data: Company
}
export const bootstrapService = {
async bootstrap(adminMode?: boolean): Promise<BootstrapResponse> {
const url = adminMode ? `${API.BOOTSTRAP}?admin_mode=1` : API.BOOTSTRAP
const { data } = await client.get(url)
return data
},
async getCurrentCompany(): Promise<CurrentCompanyResponse> {
const { data } = await client.get(API.CURRENT_COMPANY)
return data
},
}

View File

@@ -0,0 +1,100 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Company } from '@/scripts/types/domain/company'
import type { ApiResponse } from '@/scripts/types/api'
export interface UpdateCompanyPayload {
name: string
vat_id?: string | null
tax_id?: string | null
phone?: string | null
address?: {
address_street_1?: string | null
address_street_2?: string | null
city?: string | null
state?: string | null
country_id?: number | null
zip?: string | null
phone?: string | null
}
}
export interface CompanySettingsPayload {
settings: Record<string, string | number | boolean | null>
}
export interface CreateCompanyPayload {
name: string
currency?: number
address?: Record<string, unknown>
}
export const companyService = {
async update(payload: UpdateCompanyPayload): Promise<ApiResponse<Company>> {
const { data } = await client.put(API.COMPANY, payload)
return data
},
async uploadLogo(payload: FormData): Promise<ApiResponse<Company>> {
const { data } = await client.post(API.COMPANY_UPLOAD_LOGO, payload)
return data
},
async getSettings(settings?: string[]): Promise<Record<string, string>> {
const { data } = await client.get(API.COMPANY_SETTINGS, {
params: { settings },
})
return data
},
async updateSettings(payload: CompanySettingsPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.COMPANY_SETTINGS, payload)
return data
},
async hasTransactions(): Promise<{ has_transactions: boolean }> {
const { data } = await client.get(API.COMPANY_HAS_TRANSACTIONS)
return data
},
async create(payload: CreateCompanyPayload): Promise<ApiResponse<Company>> {
const { data } = await client.post(API.COMPANIES, payload)
return data
},
async listUserCompanies(): Promise<ApiResponse<Company[]>> {
const { data } = await client.get(API.COMPANIES)
return data
},
async delete(payload: { id: number }): Promise<{ success: boolean }> {
const { data } = await client.post(API.COMPANIES_DELETE, payload)
return data
},
async transferOwnership(userId: number): Promise<{ success: boolean }> {
const { data } = await client.post(`${API.TRANSFER_OWNERSHIP}/${userId}`)
return data
},
// Company Mail Configuration
async getMailDefaultConfig(): Promise<Record<string, unknown>> {
const { data } = await client.get(API.COMPANY_MAIL_DEFAULT_CONFIG)
return data
},
async getMailConfig(): Promise<Record<string, unknown>> {
const { data } = await client.get(API.COMPANY_MAIL_CONFIG)
return data
},
async saveMailConfig(payload: Record<string, unknown>): Promise<{ success: boolean }> {
const { data } = await client.post(API.COMPANY_MAIL_CONFIG, payload)
return data
},
async testMailConfig(payload: Record<string, unknown>): Promise<{ success: boolean }> {
const { data } = await client.post(API.COMPANY_MAIL_TEST, payload)
return data
},
}

View File

@@ -0,0 +1,48 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { CustomField } from '@/scripts/types/domain/custom-field'
import type { ApiResponse, ListParams } from '@/scripts/types/api'
export interface CustomFieldListParams extends ListParams {
model_type?: string
type?: string
}
export interface CreateCustomFieldPayload {
name: string
label: string
model_type: string
type: string
placeholder?: string | null
is_required?: boolean
options?: Array<{ name: string }> | string[] | null
order?: number | null
default_answer?: string | null
}
export const customFieldService = {
async list(params?: CustomFieldListParams): Promise<ApiResponse<CustomField[]>> {
const { data } = await client.get(API.CUSTOM_FIELDS, { params })
return data
},
async get(id: number): Promise<ApiResponse<CustomField>> {
const { data } = await client.get(`${API.CUSTOM_FIELDS}/${id}`)
return data
},
async create(payload: CreateCustomFieldPayload): Promise<ApiResponse<CustomField>> {
const { data } = await client.post(API.CUSTOM_FIELDS, payload)
return data
},
async update(id: number, payload: Partial<CreateCustomFieldPayload>): Promise<ApiResponse<CustomField>> {
const { data } = await client.put(`${API.CUSTOM_FIELDS}/${id}`, payload)
return data
},
async delete(id: number): Promise<{ success: boolean; error?: string }> {
const { data } = await client.delete(`${API.CUSTOM_FIELDS}/${id}`)
return data
},
}

View File

@@ -0,0 +1,84 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Customer, CreateCustomerPayload } from '@/scripts/types/domain/customer'
import type {
ApiResponse,
ListParams,
DeletePayload,
} from '@/scripts/types/api'
export interface CustomerListParams extends ListParams {
display_name?: string
}
export interface CustomerListMeta {
current_page: number
last_page: number
per_page: number
total: number
customer_total_count: number
}
export interface CustomerListResponse {
data: Customer[]
meta: CustomerListMeta
}
export interface CustomerStatsChartData {
salesTotal: number
totalReceipts: number
totalExpenses: number
netProfit: number
expenseTotals: number[]
netProfits: number[]
months: string[]
receiptTotals: number[]
invoiceTotals: number[]
}
export interface CustomerStatsParams {
previous_year?: boolean
this_year?: boolean
}
export interface CustomerStatsResponse {
data: Customer
meta: {
chartData: CustomerStatsChartData
}
}
export const customerService = {
async list(params?: CustomerListParams): Promise<CustomerListResponse> {
const { data } = await client.get(API.CUSTOMERS, { params })
return data
},
async get(id: number): Promise<ApiResponse<Customer>> {
const { data } = await client.get(`${API.CUSTOMERS}/${id}`)
return data
},
async create(payload: CreateCustomerPayload): Promise<ApiResponse<Customer>> {
const { data } = await client.post(API.CUSTOMERS, payload)
return data
},
async update(id: number, payload: Partial<CreateCustomerPayload>): Promise<ApiResponse<Customer>> {
const { data } = await client.put(`${API.CUSTOMERS}/${id}`, payload)
return data
},
async delete(payload: DeletePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.CUSTOMERS_DELETE, payload)
return data
},
async getStats(
id: number,
params?: CustomerStatsParams
): Promise<CustomerStatsResponse> {
const { data } = await client.get(`${API.CUSTOMER_STATS}/${id}/stats`, { params })
return data
},
}

View File

@@ -0,0 +1,53 @@
import { client } from '../client'
import { API } from '../endpoints'
export interface DashboardParams {
previous_year?: number
}
export interface ChartData {
months: string[]
invoice_totals: number[]
expense_totals: number[]
receipt_totals: number[]
net_income_totals: number[]
}
export interface DashboardResponse {
total_amount_due: number
total_customer_count: number
total_invoice_count: number
total_estimate_count: number
chart_data: ChartData
total_sales: string
total_receipts: string
total_expenses: string
total_net_income: string
recent_due_invoices: Array<{
id: number
invoice_number: string
due_amount: number
formatted_due_date: string
customer?: {
id: number
name: string
}
}>
recent_estimates: Array<{
id: number
estimate_number: string
total: number
status: string
customer?: {
id: number
name: string
}
}>
}
export const dashboardService = {
async load(params?: DashboardParams): Promise<DashboardResponse> {
const { data } = await client.get(API.DASHBOARD, { params })
return data
},
}

View File

@@ -0,0 +1,86 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { ApiResponse, ListParams, PaginatedResponse } from '@/scripts/types/api'
export type DiskDriverValue =
| 'local'
| 's3'
| 's3compat'
| 'doSpaces'
| 'dropbox'
export interface Disk {
id: number
name: string
type: string
driver: DiskDriverValue
set_as_default: boolean
credentials: Record<string, string> | string | null
company_id?: number | null
}
export interface DiskDriversResponse {
drivers: Array<{
name: string
value: DiskDriverValue
}>
default: DiskDriverValue | string
}
export interface CreateDiskPayload {
name: string
driver: DiskDriverValue
credentials?: Record<string, string> | string
set_as_default?: boolean
}
export interface DiskPurposes {
media_disk_id: number | null
pdf_disk_id: number | null
backup_disk_id: number | null
}
export const diskService = {
async list(params?: ListParams): Promise<PaginatedResponse<Disk>> {
const { data } = await client.get(API.DISKS, { params })
return data
},
async get(disk: DiskDriverValue): Promise<Record<string, string>> {
const { data } = await client.get(`${API.DISKS}/${disk}`)
return data
},
async create(payload: CreateDiskPayload): Promise<ApiResponse<Disk>> {
const { data } = await client.post(API.DISKS, payload)
return data
},
async update(
id: number,
payload: Partial<CreateDiskPayload>
): Promise<ApiResponse<Disk>> {
const { data } = await client.put(`${API.DISKS}/${id}`, payload)
return data
},
async delete(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.DISKS}/${id}`)
return data
},
async getDrivers(): Promise<DiskDriversResponse> {
const { data } = await client.get(API.DISK_DRIVERS)
return data
},
async getDiskPurposes(): Promise<DiskPurposes> {
const { data } = await client.get(API.DISK_PURPOSES)
return data
},
async updateDiskPurposes(payload: Partial<DiskPurposes>): Promise<{ success: boolean }> {
const { data } = await client.put(API.DISK_PURPOSES, payload)
return data
},
}

View File

@@ -0,0 +1,114 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Estimate, CreateEstimatePayload } from '@/scripts/types/domain/estimate'
import type { Invoice } from '@/scripts/types/domain/invoice'
import type {
ApiResponse,
ListParams,
DateRangeParams,
NextNumberResponse,
DeletePayload,
} from '@/scripts/types/api'
export interface EstimateListParams extends ListParams, DateRangeParams {
status?: string
customer_id?: number
}
export interface EstimateListMeta {
current_page: number
last_page: number
per_page: number
total: number
estimate_total_count: number
}
export interface EstimateListResponse {
data: Estimate[]
meta: EstimateListMeta
}
export interface SendEstimatePayload {
id: number
subject?: string
body?: string
from?: string
to?: string
is_preview?: boolean
}
export interface EstimateStatusPayload {
id: number
status: string
}
export interface EstimateTemplate {
name: string
path: string
}
export interface EstimateTemplatesResponse {
estimateTemplates: EstimateTemplate[]
}
export const estimateService = {
async list(params?: EstimateListParams): Promise<EstimateListResponse> {
const { data } = await client.get(API.ESTIMATES, { params })
return data
},
async get(id: number): Promise<ApiResponse<Estimate>> {
const { data } = await client.get(`${API.ESTIMATES}/${id}`)
return data
},
async create(payload: CreateEstimatePayload): Promise<ApiResponse<Estimate>> {
const { data } = await client.post(API.ESTIMATES, payload)
return data
},
async update(id: number, payload: Partial<CreateEstimatePayload>): Promise<ApiResponse<Estimate>> {
const { data } = await client.put(`${API.ESTIMATES}/${id}`, payload)
return data
},
async delete(payload: DeletePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.ESTIMATES_DELETE, payload)
return data
},
async send(payload: SendEstimatePayload): Promise<ApiResponse<Estimate>> {
const { data } = await client.post(`${API.ESTIMATES}/${payload.id}/send`, payload)
return data
},
async sendPreview(id: number, params?: Record<string, unknown>): Promise<ApiResponse<string>> {
const { data } = await client.get(`${API.ESTIMATES}/${id}/send/preview`, { params })
return data
},
async clone(id: number): Promise<ApiResponse<Estimate>> {
const { data } = await client.post(`${API.ESTIMATES}/${id}/clone`)
return data
},
async changeStatus(payload: EstimateStatusPayload): Promise<ApiResponse<Estimate>> {
const { data } = await client.post(`${API.ESTIMATES}/${payload.id}/status`, payload)
return data
},
async convertToInvoice(id: number): Promise<ApiResponse<Invoice>> {
const { data } = await client.post(`${API.ESTIMATES}/${id}/convert-to-invoice`)
return data
},
async getNextNumber(params?: { key?: string }): Promise<NextNumberResponse> {
const { data } = await client.get(API.NEXT_NUMBER, { params: { key: 'estimate', ...params } })
return data
},
async getTemplates(): Promise<EstimateTemplatesResponse> {
const { data } = await client.get(API.ESTIMATE_TEMPLATES)
return data
},
}

View File

@@ -0,0 +1,147 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { ExchangeRateProvider, Currency } from '@/scripts/types/domain/currency'
import type { ApiResponse, ListParams } from '@/scripts/types/api'
export interface CreateExchangeRateProviderPayload {
driver: string
key: string
active?: boolean
currencies?: string[]
driver_config?: Record<string, string>
}
// Normalized response types (what callers receive)
export interface ExchangeRateResponse {
exchangeRate: number | null
}
export interface ActiveProviderResponse {
hasActiveProvider: boolean
}
export interface SupportedCurrenciesResponse {
supportedCurrencies: string[]
}
export interface UsedCurrenciesResponse {
activeUsedCurrencies: string[]
allUsedCurrencies: string[]
}
export interface BulkCurrenciesResponse {
currencies: Array<Currency & { exchange_rate: number | null }>
}
export interface BulkUpdatePayload {
currencies: Array<{
id: number
exchange_rate: number
}>
}
export interface ConfigOption {
key: string
value: string
}
export interface ConfigDriversResponse {
exchange_rate_drivers: ConfigOption[]
}
export interface ConfigServersResponse {
currency_converter_servers: ConfigOption[]
}
export interface SupportedCurrenciesParams {
driver: string
key: string
driver_config?: Record<string, string>
}
export const exchangeRateService = {
// Providers CRUD
async listProviders(params?: ListParams): Promise<ApiResponse<ExchangeRateProvider[]>> {
const { data } = await client.get(API.EXCHANGE_RATE_PROVIDERS, { params })
return data
},
async getProvider(id: number): Promise<ApiResponse<ExchangeRateProvider>> {
const { data } = await client.get(`${API.EXCHANGE_RATE_PROVIDERS}/${id}`)
return data
},
async createProvider(payload: CreateExchangeRateProviderPayload): Promise<ApiResponse<ExchangeRateProvider>> {
const { data } = await client.post(API.EXCHANGE_RATE_PROVIDERS, payload)
return data
},
async updateProvider(
id: number,
payload: Partial<CreateExchangeRateProviderPayload>,
): Promise<ApiResponse<ExchangeRateProvider>> {
const { data } = await client.put(`${API.EXCHANGE_RATE_PROVIDERS}/${id}`, payload)
return data
},
async deleteProvider(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.EXCHANGE_RATE_PROVIDERS}/${id}`)
return data
},
// Exchange Rates
// Backend returns { exchangeRate: [number] } or { error: string }
async getRate(currencyId: number): Promise<ExchangeRateResponse> {
const { data } = await client.get(`${API.CURRENCIES}/${currencyId}/exchange-rate`)
const raw = data as Record<string, unknown>
if (raw.exchangeRate && Array.isArray(raw.exchangeRate)) {
return { exchangeRate: Number(raw.exchangeRate[0]) ?? null }
}
return { exchangeRate: null }
},
// Backend returns { success: true, message: "provider_active" } or { error: "no_active_provider" }
async getActiveProvider(currencyId: number): Promise<ActiveProviderResponse> {
const { data } = await client.get(`${API.CURRENCIES}/${currencyId}/active-provider`)
const raw = data as Record<string, unknown>
return { hasActiveProvider: raw.success === true }
},
// Currency lists
async getSupportedCurrencies(params: SupportedCurrenciesParams): Promise<SupportedCurrenciesResponse> {
const { data } = await client.get(API.SUPPORTED_CURRENCIES, { params })
return data
},
// Backend returns { activeUsedCurrencies: string[], allUsedCurrencies: string[] }
async getUsedCurrencies(params?: { provider_id?: number }): Promise<UsedCurrenciesResponse> {
const { data } = await client.get(API.USED_CURRENCIES, { params })
return data
},
async getBulkCurrencies(): Promise<BulkCurrenciesResponse> {
const { data } = await client.get(API.CURRENCIES_USED)
return data
},
async bulkUpdateExchangeRate(payload: BulkUpdatePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.CURRENCIES_BULK_UPDATE, payload)
return data
},
// Config
// Backend returns { exchange_rate_drivers: Array<{ key, value }> }
async getDrivers(): Promise<ConfigDriversResponse> {
const { data } = await client.get(API.CONFIG, { params: { key: 'exchange_rate_drivers' } })
return data
},
// Backend returns { currency_converter_servers: Array<{ key, value }> }
async getCurrencyConverterServers(): Promise<ConfigServersResponse> {
const { data } = await client.get(API.CONFIG, { params: { key: 'currency_converter_servers' } })
return data
},
}

View File

@@ -0,0 +1,100 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Expense, ExpenseCategory, CreateExpensePayload } from '@/scripts/types/domain/expense'
import type {
ApiResponse,
ListParams,
DateRangeParams,
DeletePayload,
} from '@/scripts/types/api'
export interface ExpenseListParams extends ListParams, DateRangeParams {
expense_category_id?: number
customer_id?: number
}
export interface ExpenseListMeta {
current_page: number
last_page: number
per_page: number
total: number
expense_total_count: number
}
export interface ExpenseListResponse {
data: Expense[]
meta: ExpenseListMeta
}
export interface CreateExpenseCategoryPayload {
name: string
description?: string | null
}
export const expenseService = {
async list(params?: ExpenseListParams): Promise<ExpenseListResponse> {
const { data } = await client.get(API.EXPENSES, { params })
return data
},
async get(id: number): Promise<ApiResponse<Expense>> {
const { data } = await client.get(`${API.EXPENSES}/${id}`)
return data
},
async create(payload: FormData): Promise<ApiResponse<Expense>> {
const { data } = await client.post(API.EXPENSES, payload)
return data
},
async update(id: number, payload: FormData): Promise<ApiResponse<Expense>> {
const { data } = await client.post(`${API.EXPENSES}/${id}`, payload)
return data
},
async delete(payload: DeletePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.EXPENSES_DELETE, payload)
return data
},
async showReceipt(id: number): Promise<Blob> {
const { data } = await client.get(`${API.EXPENSES}/${id}/show/receipt`, {
responseType: 'blob',
})
return data
},
async uploadReceipt(id: number, payload: FormData): Promise<ApiResponse<Expense>> {
const { data } = await client.post(`${API.EXPENSES}/${id}/upload/receipts`, payload)
return data
},
// Expense Categories
async listCategories(params?: ListParams): Promise<ApiResponse<ExpenseCategory[]>> {
const { data } = await client.get(API.CATEGORIES, { params })
return data
},
async getCategory(id: number): Promise<ApiResponse<ExpenseCategory>> {
const { data } = await client.get(`${API.CATEGORIES}/${id}`)
return data
},
async createCategory(payload: CreateExpenseCategoryPayload): Promise<ApiResponse<ExpenseCategory>> {
const { data } = await client.post(API.CATEGORIES, payload)
return data
},
async updateCategory(
id: number,
payload: CreateExpenseCategoryPayload,
): Promise<ApiResponse<ExpenseCategory>> {
const { data } = await client.put(`${API.CATEGORIES}/${id}`, payload)
return data
},
async deleteCategory(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.CATEGORIES}/${id}`)
return data
},
}

View File

@@ -0,0 +1,60 @@
export { authService } from './auth.service'
export { bootstrapService } from './bootstrap.service'
export { invoiceService } from './invoice.service'
export { estimateService } from './estimate.service'
export { recurringInvoiceService } from './recurring-invoice.service'
export { customerService } from './customer.service'
export { paymentService } from './payment.service'
export { expenseService } from './expense.service'
export { itemService } from './item.service'
export { companyService } from './company.service'
export { userService } from './user.service'
export { memberService } from './member.service'
export { settingService } from './setting.service'
export { dashboardService } from './dashboard.service'
export { reportService } from './report.service'
export { roleService } from './role.service'
export { taxTypeService } from './tax-type.service'
export { customFieldService } from './custom-field.service'
export { noteService } from './note.service'
export { exchangeRateService } from './exchange-rate.service'
export { moduleService } from './module.service'
export { backupService } from './backup.service'
export { mailService } from './mail.service'
export { pdfService } from './pdf.service'
export { diskService } from './disk.service'
export { updateService } from './update.service'
// Re-export service types for convenience
export type { LoginPayload, LoginResponse, ForgotPasswordPayload, ResetPasswordPayload, RegisterWithInvitationPayload } from './auth.service'
export type { BootstrapResponse, MenuItem, CurrentCompanyResponse } from './bootstrap.service'
export type { InvoiceListParams, InvoiceListResponse, SendInvoicePayload, InvoiceStatusPayload, InvoiceTemplatesResponse } from './invoice.service'
export type { EstimateListParams, EstimateListResponse, SendEstimatePayload, EstimateStatusPayload, EstimateTemplatesResponse } from './estimate.service'
export type { RecurringInvoiceListParams, RecurringInvoiceListResponse, FrequencyDateParams, FrequencyDateResponse } from './recurring-invoice.service'
export type {
CustomerListParams,
CustomerListResponse,
CustomerStatsChartData,
CustomerStatsParams,
CustomerStatsResponse,
} from './customer.service'
export type { PaymentListParams, PaymentListResponse, SendPaymentPayload, CreatePaymentMethodPayload } from './payment.service'
export type { ExpenseListParams, ExpenseListResponse, CreateExpenseCategoryPayload } from './expense.service'
export type { ItemListParams, ItemListResponse, CreateItemPayload, CreateUnitPayload } from './item.service'
export type { UpdateCompanyPayload, CompanySettingsPayload, CreateCompanyPayload } from './company.service'
export type { UpdateProfilePayload, UserSettingsPayload } from './user.service'
export type { MemberListParams, MemberListResponse, UpdateMemberPayload, InviteMemberPayload, DeleteMembersPayload } from './member.service'
export type { ConfigResponse, GlobalSettingsPayload, DateFormat, TimeFormat } from './setting.service'
export type { DashboardParams, DashboardResponse, ChartData } from './dashboard.service'
export type { ReportParams, SalesReportResponse, ProfitLossReportResponse, ExpenseReportResponse, TaxReportResponse } from './report.service'
export type { CreateRolePayload, AbilitiesResponse } from './role.service'
export type { CreateTaxTypePayload } from './tax-type.service'
export type { CustomFieldListParams, CreateCustomFieldPayload } from './custom-field.service'
export type { CreateNotePayload } from './note.service'
export type { CreateExchangeRateProviderPayload, BulkUpdatePayload, ExchangeRateResponse, ActiveProviderResponse } from './exchange-rate.service'
export type { Module, ModuleInstallPayload, ModuleCheckResponse } from './module.service'
export type { Backup, BackupListResponse, CreateBackupPayload, DeleteBackupParams } from './backup.service'
export type { MailConfig, MailConfigResponse, MailDriver, SmtpConfig, MailgunConfig, SesConfig, TestMailPayload } from './mail.service'
export type { PdfConfig, PdfConfigResponse, PdfDriver, DomPdfConfig, GotenbergConfig } from './pdf.service'
export type { Disk, DiskDriversResponse, DiskDriverValue, CreateDiskPayload } from './disk.service'
export type { CheckUpdateResponse, UpdateRelease, UpdateDownloadResponse, UpdateStepResponse, FinishUpdatePayload } from './update.service'

View File

@@ -0,0 +1,125 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Invoice, CreateInvoicePayload } from '@/scripts/types/domain/invoice'
import type {
ApiResponse,
PaginatedResponse,
ListParams,
DateRangeParams,
NextNumberResponse,
DeletePayload,
} from '@/scripts/types/api'
export interface InvoiceListParams extends ListParams, DateRangeParams {
status?: string
customer_id?: number
}
export interface InvoiceListMeta {
current_page: number
last_page: number
per_page: number
total: number
invoice_total_count: number
}
export interface InvoiceListResponse {
data: Invoice[]
meta: InvoiceListMeta
}
export interface SendInvoicePayload {
id: number
subject?: string | null
body?: string | null
from?: string | null
to?: string | null
cc?: string | null
bcc?: string | null
}
export interface InvoiceStatusPayload {
id: number
status: string
}
export interface SendPreviewParams {
id: number
from?: string | null
to?: string | null
cc?: string | null
bcc?: string | null
subject?: string | null
body?: string | null
}
export interface InvoiceTemplate {
name: string
path: string
}
export interface InvoiceTemplatesResponse {
invoiceTemplates: InvoiceTemplate[]
}
export const invoiceService = {
async list(params?: InvoiceListParams): Promise<InvoiceListResponse> {
const { data } = await client.get(API.INVOICES, { params })
return data
},
async get(id: number): Promise<ApiResponse<Invoice>> {
const { data } = await client.get(`${API.INVOICES}/${id}`)
return data
},
async create(payload: CreateInvoicePayload): Promise<ApiResponse<Invoice>> {
const { data } = await client.post(API.INVOICES, payload)
return data
},
async update(id: number, payload: Partial<CreateInvoicePayload>): Promise<ApiResponse<Invoice>> {
const { data } = await client.put(`${API.INVOICES}/${id}`, payload)
return data
},
async delete(payload: DeletePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.INVOICES_DELETE, payload)
return data
},
async send(payload: SendInvoicePayload): Promise<ApiResponse<Invoice>> {
const { data } = await client.post(`${API.INVOICES}/${payload.id}/send`, payload)
return data
},
async sendPreview(params: SendPreviewParams): Promise<ApiResponse<string>> {
const { data } = await client.get(`${API.INVOICES}/${params.id}/send/preview`, { params })
return data
},
async clone(id: number): Promise<ApiResponse<Invoice>> {
const { data } = await client.post(`${API.INVOICES}/${id}/clone`)
return data
},
async convertToEstimate(id: number): Promise<ApiResponse<Record<string, unknown>>> {
const { data } = await client.post(`${API.INVOICES}/${id}/convert-to-estimate`)
return data
},
async changeStatus(payload: InvoiceStatusPayload): Promise<ApiResponse<Invoice>> {
const { data } = await client.post(`${API.INVOICES}/${payload.id}/status`, payload)
return data
},
async getNextNumber(params?: { key?: string }): Promise<NextNumberResponse> {
const { data } = await client.get(API.NEXT_NUMBER, { params: { key: 'invoice', ...params } })
return data
},
async getTemplates(): Promise<InvoiceTemplatesResponse> {
const { data } = await client.get(API.INVOICE_TEMPLATES)
return data
},
}

View File

@@ -0,0 +1,90 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Item, Unit } from '@/scripts/types/domain/item'
import type {
ApiResponse,
ListParams,
DeletePayload,
} from '@/scripts/types/api'
export interface ItemListParams extends ListParams {
filter?: Record<string, unknown>
}
export interface ItemListMeta {
current_page: number
last_page: number
per_page: number
total: number
item_total_count: number
}
export interface ItemListResponse {
data: Item[]
meta: ItemListMeta
}
export interface CreateItemPayload {
name: string
description?: string | null
price: number
unit_id?: number | null
taxes?: Array<{ tax_type_id: number }>
}
export interface CreateUnitPayload {
name: string
}
export const itemService = {
async list(params?: ItemListParams): Promise<ItemListResponse> {
const { data } = await client.get(API.ITEMS, { params })
return data
},
async get(id: number): Promise<ApiResponse<Item>> {
const { data } = await client.get(`${API.ITEMS}/${id}`)
return data
},
async create(payload: CreateItemPayload): Promise<ApiResponse<Item>> {
const { data } = await client.post(API.ITEMS, payload)
return data
},
async update(id: number, payload: Partial<CreateItemPayload>): Promise<ApiResponse<Item>> {
const { data } = await client.put(`${API.ITEMS}/${id}`, payload)
return data
},
async delete(payload: DeletePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.ITEMS_DELETE, payload)
return data
},
// Units
async listUnits(params?: ListParams): Promise<ApiResponse<Unit[]>> {
const { data } = await client.get(API.UNITS, { params })
return data
},
async getUnit(id: number): Promise<ApiResponse<Unit>> {
const { data } = await client.get(`${API.UNITS}/${id}`)
return data
},
async createUnit(payload: CreateUnitPayload): Promise<ApiResponse<Unit>> {
const { data } = await client.post(API.UNITS, payload)
return data
},
async updateUnit(id: number, payload: CreateUnitPayload): Promise<ApiResponse<Unit>> {
const { data } = await client.put(`${API.UNITS}/${id}`, payload)
return data
},
async deleteUnit(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.UNITS}/${id}`)
return data
},
}

View File

@@ -0,0 +1,70 @@
import { client } from '../client'
import { API } from '../endpoints'
export type MailDriver = string
export interface SmtpConfig {
mail_driver: string
mail_host: string
mail_port: number | null
mail_username: string
mail_password: string
mail_encryption: string
from_mail: string
from_name: string
}
export interface MailgunConfig {
mail_driver: string
mail_mailgun_domain: string
mail_mailgun_secret: string
mail_mailgun_endpoint: string
from_mail: string
from_name: string
}
export interface SesConfig {
mail_driver: string
mail_host: string
mail_port: number | null
mail_ses_key: string
mail_ses_secret: string
mail_ses_region: string
from_mail: string
from_name: string
}
export type MailConfig = SmtpConfig | MailgunConfig | SesConfig
export interface MailConfigResponse {
mail_driver: string
[key: string]: unknown
}
export interface TestMailPayload {
to: string
subject: string
message: string
}
export const mailService = {
async getDrivers(): Promise<MailDriver[]> {
const { data } = await client.get(API.MAIL_DRIVERS)
return data
},
async getConfig(): Promise<MailConfigResponse> {
const { data } = await client.get(API.MAIL_CONFIG)
return data
},
async saveConfig(payload: MailConfig): Promise<{ success?: string; error?: string }> {
const { data } = await client.post(API.MAIL_CONFIG, payload)
return data
},
async testEmail(payload: TestMailPayload): Promise<{ success?: boolean; error?: string }> {
const { data } = await client.post(API.MAIL_TEST, payload)
return data
},
}

View File

@@ -0,0 +1,102 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { User } from '@/scripts/types/domain/user'
import type { CompanyInvitation } from '@/scripts/types/domain/company'
import type { ApiResponse, PaginatedResponse, ListParams } from '@/scripts/types/api'
export interface MemberListParams extends ListParams {
display_name?: string
}
export interface MemberListResponse {
data: User[]
meta: {
current_page: number
last_page: number
per_page: number
total: number
}
}
export interface UpdateMemberPayload {
name?: string
email?: string
phone?: string | null
role?: string | null
companies?: Array<{
id: number
role?: string
}>
}
export interface InviteMemberPayload {
email: string
role_id: number | null
}
export interface DeleteMembersPayload {
users: number[]
}
export interface PendingInvitationsResponse {
invitations: CompanyInvitation[]
}
export const memberService = {
async list(params?: MemberListParams): Promise<MemberListResponse> {
const { data } = await client.get(API.MEMBERS, { params })
return data
},
async get(id: number): Promise<ApiResponse<User>> {
const { data } = await client.get(`${API.MEMBERS}/${id}`)
return data
},
async create(payload: UpdateMemberPayload): Promise<ApiResponse<User>> {
const { data } = await client.post(API.MEMBERS, payload)
return data
},
async update(id: number, payload: UpdateMemberPayload): Promise<ApiResponse<User>> {
const { data } = await client.put(`${API.MEMBERS}/${id}`, payload)
return data
},
async delete(payload: DeleteMembersPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.MEMBERS_DELETE, payload)
return data
},
// Company Invitations (send invitations)
async fetchPendingInvitations(): Promise<PendingInvitationsResponse> {
const { data } = await client.get(API.COMPANY_INVITATIONS)
return data
},
async invite(payload: InviteMemberPayload): Promise<ApiResponse<CompanyInvitation>> {
const { data } = await client.post(API.COMPANY_INVITATIONS, payload)
return data
},
async cancelInvitation(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.COMPANY_INVITATIONS}/${id}`)
return data
},
// User-scoped invitation responses
async fetchUserPendingInvitations(): Promise<PendingInvitationsResponse> {
const { data } = await client.get(API.INVITATIONS_PENDING)
return data
},
async acceptInvitation(token: string): Promise<{ success: boolean }> {
const { data } = await client.post(`${API.INVITATIONS}/${token}/accept`)
return data
},
async declineInvitation(token: string): Promise<{ success: boolean }> {
const { data } = await client.post(`${API.INVITATIONS}/${token}/decline`)
return data
},
}

View File

@@ -0,0 +1,77 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { ApiResponse } from '@/scripts/types/api'
export interface Module {
name: string
slug: string
description: string
version: string
enabled: boolean
installed: boolean
[key: string]: unknown
}
export interface ModuleCheckResponse {
error?: string
success?: boolean
}
export interface ModuleInstallPayload {
module: string
version: string
api_token?: string
}
export const moduleService = {
async list(): Promise<ApiResponse<Module[]>> {
const { data } = await client.get(API.MODULES)
return data
},
async get(module: string): Promise<Module> {
const { data } = await client.get(`${API.MODULES}/${module}`)
return data
},
async checkToken(apiToken: string): Promise<ModuleCheckResponse> {
const { data } = await client.get(`${API.MODULES_CHECK}?api_token=${apiToken}`)
return data
},
async enable(module: string): Promise<{ success: boolean }> {
const { data } = await client.post(`${API.MODULES}/${module}/enable`)
return data
},
async disable(module: string): Promise<{ success: boolean }> {
const { data } = await client.post(`${API.MODULES}/${module}/disable`)
return data
},
// Installation flow
async download(payload: ModuleInstallPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.MODULES_DOWNLOAD, payload)
return data
},
async upload(payload: FormData): Promise<{ success: boolean }> {
const { data } = await client.post(API.MODULES_UPLOAD, payload)
return data
},
async unzip(payload: ModuleInstallPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.MODULES_UNZIP, payload)
return data
},
async copy(payload: ModuleInstallPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.MODULES_COPY, payload)
return data
},
async complete(payload: ModuleInstallPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.MODULES_COMPLETE, payload)
return data
},
}

View File

@@ -0,0 +1,38 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Note } from '@/scripts/types/domain/note'
import type { ApiResponse, ListParams } from '@/scripts/types/api'
export interface CreateNotePayload {
type: string
name: string
notes: string
is_default?: boolean
}
export const noteService = {
async list(params?: ListParams): Promise<ApiResponse<Note[]>> {
const { data } = await client.get(API.NOTES, { params })
return data
},
async get(id: number): Promise<ApiResponse<Note>> {
const { data } = await client.get(`${API.NOTES}/${id}`)
return data
},
async create(payload: CreateNotePayload): Promise<Note> {
const { data } = await client.post(API.NOTES, payload)
return data
},
async update(id: number, payload: Partial<CreateNotePayload>): Promise<ApiResponse<Note>> {
const { data } = await client.put(`${API.NOTES}/${id}`, payload)
return data
},
async delete(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.NOTES}/${id}`)
return data
},
}

View File

@@ -0,0 +1,108 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Payment, PaymentMethod, CreatePaymentPayload } from '@/scripts/types/domain/payment'
import type {
ApiResponse,
ListParams,
NextNumberResponse,
DeletePayload,
} from '@/scripts/types/api'
export interface PaymentListParams extends ListParams {
customer_id?: number
from_date?: string
to_date?: string
}
export interface PaymentListMeta {
current_page: number
last_page: number
per_page: number
total: number
payment_total_count: number
}
export interface PaymentListResponse {
data: Payment[]
meta: PaymentListMeta
}
export interface SendPaymentPayload {
id: number
subject?: string
body?: string
from?: string
to?: string
}
export interface CreatePaymentMethodPayload {
name: string
}
export const paymentService = {
async list(params?: PaymentListParams): Promise<PaymentListResponse> {
const { data } = await client.get(API.PAYMENTS, { params })
return data
},
async get(id: number): Promise<ApiResponse<Payment>> {
const { data } = await client.get(`${API.PAYMENTS}/${id}`)
return data
},
async create(payload: CreatePaymentPayload): Promise<ApiResponse<Payment>> {
const { data } = await client.post(API.PAYMENTS, payload)
return data
},
async update(id: number, payload: Partial<CreatePaymentPayload>): Promise<ApiResponse<Payment>> {
const { data } = await client.put(`${API.PAYMENTS}/${id}`, payload)
return data
},
async delete(payload: DeletePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.PAYMENTS_DELETE, payload)
return data
},
async send(payload: SendPaymentPayload): Promise<ApiResponse<Payment>> {
const { data } = await client.post(`${API.PAYMENTS}/${payload.id}/send`, payload)
return data
},
async sendPreview(id: number, params?: Record<string, unknown>): Promise<ApiResponse<string>> {
const { data } = await client.get(`${API.PAYMENTS}/${id}/send/preview`, { params })
return data
},
async getNextNumber(params?: { key?: string }): Promise<NextNumberResponse> {
const { data } = await client.get(API.NEXT_NUMBER, { params: { key: 'payment', ...params } })
return data
},
// Payment Methods
async listMethods(params?: ListParams): Promise<ApiResponse<PaymentMethod[]>> {
const { data } = await client.get(API.PAYMENT_METHODS, { params })
return data
},
async getMethod(id: number): Promise<ApiResponse<PaymentMethod>> {
const { data } = await client.get(`${API.PAYMENT_METHODS}/${id}`)
return data
},
async createMethod(payload: CreatePaymentMethodPayload): Promise<ApiResponse<PaymentMethod>> {
const { data } = await client.post(API.PAYMENT_METHODS, payload)
return data
},
async updateMethod(id: number, payload: CreatePaymentMethodPayload): Promise<ApiResponse<PaymentMethod>> {
const { data } = await client.put(`${API.PAYMENT_METHODS}/${id}`, payload)
return data
},
async deleteMethod(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.PAYMENT_METHODS}/${id}`)
return data
},
}

View File

@@ -0,0 +1,38 @@
import { client } from '../client'
import { API } from '../endpoints'
export type PdfDriver = string
export interface DomPdfConfig {
pdf_driver: string
}
export interface GotenbergConfig {
pdf_driver: string
gotenberg_host: string
gotenberg_papersize: string
}
export type PdfConfig = DomPdfConfig | GotenbergConfig
export interface PdfConfigResponse {
pdf_driver: string
[key: string]: unknown
}
export const pdfService = {
async getDrivers(): Promise<PdfDriver[]> {
const { data } = await client.get(API.PDF_DRIVERS)
return data
},
async getConfig(): Promise<PdfConfigResponse> {
const { data } = await client.get(API.PDF_CONFIG)
return data
},
async saveConfig(payload: PdfConfig): Promise<{ success?: string; error?: string }> {
const { data } = await client.post(API.PDF_CONFIG, payload)
return data
},
}

View File

@@ -0,0 +1,70 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { RecurringInvoice, CreateRecurringInvoicePayload } from '@/scripts/types/domain/recurring-invoice'
import type {
ApiResponse,
ListParams,
DeletePayload,
} from '@/scripts/types/api'
export interface RecurringInvoiceListParams extends ListParams {
status?: string
customer_id?: number
}
export interface RecurringInvoiceListMeta {
current_page: number
last_page: number
per_page: number
total: number
recurring_invoice_total_count: number
}
export interface RecurringInvoiceListResponse {
data: RecurringInvoice[]
meta: RecurringInvoiceListMeta
}
export interface FrequencyDateParams {
frequency: string
starts_at?: string
}
export interface FrequencyDateResponse {
next_invoice_at: string
}
export const recurringInvoiceService = {
async list(params?: RecurringInvoiceListParams): Promise<RecurringInvoiceListResponse> {
const { data } = await client.get(API.RECURRING_INVOICES, { params })
return data
},
async get(id: number): Promise<ApiResponse<RecurringInvoice>> {
const { data } = await client.get(`${API.RECURRING_INVOICES}/${id}`)
return data
},
async create(payload: CreateRecurringInvoicePayload): Promise<ApiResponse<RecurringInvoice>> {
const { data } = await client.post(API.RECURRING_INVOICES, payload)
return data
},
async update(
id: number,
payload: Partial<CreateRecurringInvoicePayload>,
): Promise<ApiResponse<RecurringInvoice>> {
const { data } = await client.put(`${API.RECURRING_INVOICES}/${id}`, payload)
return data
},
async delete(payload: DeletePayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.RECURRING_INVOICES_DELETE, payload)
return data
},
async getFrequencyDate(params: FrequencyDateParams): Promise<FrequencyDateResponse> {
const { data } = await client.get(API.RECURRING_INVOICE_FREQUENCY, { params })
return data
},
}

View File

@@ -0,0 +1,86 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { DateRangeParams } from '@/scripts/types/api'
export interface ReportParams extends DateRangeParams {
report_type?: string
}
export interface SalesReportResponse {
data: Array<{
date: string
total: number
count: number
}>
total: number
from_date: string
to_date: string
}
export interface ProfitLossReportResponse {
data: {
income: Array<{
label: string
amount: number
}>
expenses: Array<{
label: string
amount: number
}>
net_profit: number
}
from_date: string
to_date: string
}
export interface ExpenseReportResponse {
data: Array<{
category: string
total: number
count: number
}>
total: number
from_date: string
to_date: string
}
export interface TaxReportResponse {
data: Array<{
tax_name: string
tax_amount: number
invoice_count: number
}>
total: number
from_date: string
to_date: string
}
export const reportService = {
async getSalesReport(params: ReportParams): Promise<SalesReportResponse> {
const { data } = await client.get(API.DASHBOARD, {
params: { ...params, report_type: 'sales' },
})
return data
},
async getProfitLossReport(params: ReportParams): Promise<ProfitLossReportResponse> {
const { data } = await client.get(API.DASHBOARD, {
params: { ...params, report_type: 'profit_loss' },
})
return data
},
async getExpenseReport(params: ReportParams): Promise<ExpenseReportResponse> {
const { data } = await client.get(API.DASHBOARD, {
params: { ...params, report_type: 'expenses' },
})
return data
},
async getTaxReport(params: ReportParams): Promise<TaxReportResponse> {
const { data } = await client.get(API.DASHBOARD, {
params: { ...params, report_type: 'tax' },
})
return data
},
}

View File

@@ -0,0 +1,48 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Role, Ability } from '@/scripts/types/domain/role'
import type { ApiResponse, ListParams } from '@/scripts/types/api'
export interface CreateRolePayload {
name: string
abilities: Array<{
ability: string
model?: string | null
}>
}
export interface AbilitiesResponse {
abilities: Ability[]
}
export const roleService = {
async list(params?: ListParams): Promise<ApiResponse<Role[]>> {
const { data } = await client.get(API.ROLES, { params })
return data
},
async get(id: number): Promise<ApiResponse<Role>> {
const { data } = await client.get(`${API.ROLES}/${id}`)
return data
},
async create(payload: CreateRolePayload): Promise<{ role: Role }> {
const { data } = await client.post(API.ROLES, payload)
return data
},
async update(id: number, payload: Partial<CreateRolePayload>): Promise<ApiResponse<Role>> {
const { data } = await client.put(`${API.ROLES}/${id}`, payload)
return data
},
async delete(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.ROLES}/${id}`)
return data
},
async getAbilities(params?: ListParams): Promise<AbilitiesResponse> {
const { data } = await client.get(API.ABILITIES, { params })
return data
},
}

View File

@@ -0,0 +1,109 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { Country } from '@/scripts/types/domain/customer'
import type { Currency } from '@/scripts/types/domain/currency'
export interface DateFormat {
display_date: string
carbon_format_value: string
moment_format_value: string
}
export interface TimeFormat {
display_time: string
carbon_format_value: string
moment_format_value: string
}
export interface ConfigResponse {
[key: string]: unknown
}
export interface GlobalSettingsPayload {
settings: Record<string, string | number | boolean | null>
}
export interface NumberPlaceholdersParams {
key: string
}
export interface NumberPlaceholder {
name: string
value: string
}
export const settingService = {
// Global Settings (admin-level)
async getGlobalSettings(): Promise<Record<string, string>> {
const { data } = await client.get(API.SETTINGS)
return data
},
async updateGlobalSettings(payload: GlobalSettingsPayload): Promise<{ success: boolean }> {
const { data } = await client.post(API.SETTINGS, payload)
return data
},
// Config
async getConfig(params?: Record<string, string>): Promise<ConfigResponse> {
const { data } = await client.get(API.CONFIG, { params })
return data
},
// Reference Data
async getCountries(): Promise<{ data: Country[] }> {
const { data } = await client.get(API.COUNTRIES)
return data
},
async getCurrencies(): Promise<{ data: Currency[] }> {
const { data } = await client.get(API.CURRENCIES)
return data
},
async getTimezones(): Promise<{ time_zones: string[] }> {
const { data } = await client.get(API.TIMEZONES)
return data
},
async getDateFormats(): Promise<{ date_formats: DateFormat[] }> {
const { data } = await client.get(API.DATE_FORMATS)
return data
},
async getTimeFormats(): Promise<{ time_formats: TimeFormat[] }> {
const { data } = await client.get(API.TIME_FORMATS)
return data
},
// Serial Numbers
async getNextNumber(params: { key: string }): Promise<{ nextNumber: string }> {
const { data } = await client.get(API.NEXT_NUMBER, { params })
return data
},
async getNumberPlaceholders(params: NumberPlaceholdersParams): Promise<{ placeholders: NumberPlaceholder[] }> {
const { data } = await client.get(API.NUMBER_PLACEHOLDERS, { params })
return data
},
// Search
async search(params: { search: string }): Promise<{
users: { data: unknown[] }
customers: { data: unknown[] }
}> {
const { data } = await client.get(API.SEARCH, { params })
return data
},
async searchUsers(params: { search: string }): Promise<{ data: unknown[] }> {
const { data } = await client.get(API.SEARCH_USERS, { params })
return data
},
// App Version
async getAppVersion(): Promise<{ version: string; channel: string }> {
const { data } = await client.get(API.APP_VERSION)
return data
},
}

View File

@@ -0,0 +1,41 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { TaxType } from '@/scripts/types/domain/tax'
import type { ApiResponse, ListParams } from '@/scripts/types/api'
export interface CreateTaxTypePayload {
name: string
percent: number
fixed_amount?: number
calculation_type?: string | null
compound_tax?: boolean
collective_tax?: number | null
description?: string | null
}
export const taxTypeService = {
async list(params?: ListParams): Promise<ApiResponse<TaxType[]>> {
const { data } = await client.get(API.TAX_TYPES, { params })
return data
},
async get(id: number): Promise<ApiResponse<TaxType>> {
const { data } = await client.get(`${API.TAX_TYPES}/${id}`)
return data
},
async create(payload: CreateTaxTypePayload): Promise<ApiResponse<TaxType>> {
const { data } = await client.post(API.TAX_TYPES, payload)
return data
},
async update(id: number, payload: Partial<CreateTaxTypePayload>): Promise<ApiResponse<TaxType>> {
const { data } = await client.put(`${API.TAX_TYPES}/${id}`, payload)
return data
},
async delete(id: number): Promise<{ success: boolean }> {
const { data } = await client.delete(`${API.TAX_TYPES}/${id}`)
return data
},
}

View File

@@ -0,0 +1,79 @@
import { client } from '../client'
import { API } from '../endpoints'
export interface UpdateRelease {
version: string
description?: string | null
changelog?: string | null
extensions?: Record<string, boolean>
min_php_version?: string | null
deleted_files?: string | string[] | null
}
export interface CheckUpdateResponse {
success?: boolean
release: UpdateRelease | null
is_minor?: boolean
}
export interface UpdateDownloadResponse {
success: boolean
path?: string | boolean | Record<string, unknown> | null
}
export interface UpdateStepResponse {
success: boolean
path?: string | boolean | Record<string, unknown> | null
error?: string | boolean
data?: Record<string, unknown>
}
export interface FinishUpdatePayload {
installed: string
version: string
}
export const updateService = {
async check(channel: 'stable' | 'insider' = 'stable'): Promise<CheckUpdateResponse> {
const { data } = await client.get(API.CHECK_UPDATE, {
params: { channel },
})
return data
},
async download(payload: { version: string }): Promise<UpdateDownloadResponse> {
const { data } = await client.post(API.UPDATE_DOWNLOAD, payload)
return data
},
async unzip(payload: { path: string }): Promise<UpdateStepResponse> {
const { data } = await client.post(API.UPDATE_UNZIP, payload)
return data
},
async copy(payload: { path: string }): Promise<UpdateStepResponse> {
const { data } = await client.post(API.UPDATE_COPY, payload)
return data
},
async delete(payload: { deleted_files?: string | string[] | null }): Promise<UpdateStepResponse> {
const { data } = await client.post(API.UPDATE_DELETE, payload)
return data
},
async clean(payload?: { deleted_files?: string | string[] | null }): Promise<UpdateStepResponse> {
const { data } = await client.post(API.UPDATE_CLEAN, payload ?? {})
return data
},
async migrate(): Promise<UpdateStepResponse> {
const { data } = await client.post(API.UPDATE_MIGRATE)
return data
},
async finish(payload: FinishUpdatePayload): Promise<UpdateStepResponse> {
const { data } = await client.post(API.UPDATE_FINISH, payload)
return data
},
}

View File

@@ -0,0 +1,48 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { User } from '@/scripts/types/domain/user'
import type { ApiResponse } from '@/scripts/types/api'
export interface UpdateProfilePayload {
name: string
email: string
password?: string | null
confirm_password?: string | null
}
export interface UserSettingsPayload {
settings: Record<string, string | number | boolean | null>
}
export interface UserSettingsResponse {
[key: string]: string | null
}
export const userService = {
async getProfile(): Promise<ApiResponse<User>> {
const { data } = await client.get(API.ME)
return data
},
async updateProfile(payload: UpdateProfilePayload): Promise<ApiResponse<User>> {
const { data } = await client.put(API.ME, payload)
return data
},
async getSettings(settings?: string[]): Promise<UserSettingsResponse> {
const { data } = await client.get(API.ME_SETTINGS, {
params: { settings },
})
return data
},
async updateSettings(payload: UserSettingsPayload): Promise<{ success: boolean }> {
const { data } = await client.put(API.ME_SETTINGS, payload)
return data
},
async uploadAvatar(payload: FormData): Promise<ApiResponse<User>> {
const { data } = await client.post(API.ME_UPLOAD_AVATAR, payload)
return data
},
}