Initial commit: Meshkee dashboards monorepo.
Includes business, customer, and super-admin apps with shared packages and production deploy scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import { apiRequest, setTokens, clearTokens } from '../lib/api'
|
||||
import type { LoginResponse, MeResponse } from '../types/auth'
|
||||
|
||||
export async function login(cellNumber: string, password: string) {
|
||||
const data = await apiRequest<LoginResponse>('/auth/login', {
|
||||
method: 'POST',
|
||||
body: { cellNumber, password },
|
||||
})
|
||||
|
||||
setTokens(data.accessToken, data.refreshToken)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchCurrentUser(signal?: AbortSignal) {
|
||||
return apiRequest<MeResponse>('/auth/me', { auth: true, signal })
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
clearTokens()
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import type { BusinessesListResponse, BusinessListItem, CreateBusinessPayload } from '../types/business'
|
||||
|
||||
export interface ListBusinessesParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
name?: string
|
||||
domain?: string
|
||||
category?: string
|
||||
}
|
||||
|
||||
export interface UpdateBusinessPayload {
|
||||
name?: string
|
||||
slug?: string
|
||||
}
|
||||
|
||||
export interface AddDomainPayload {
|
||||
host: string
|
||||
isPrimary?: boolean
|
||||
}
|
||||
|
||||
export interface DisableBusinessPayload {
|
||||
isActive: boolean
|
||||
}
|
||||
|
||||
export interface UpdateDomainPayload {
|
||||
host: string
|
||||
}
|
||||
|
||||
export async function listBusinesses(params: ListBusinessesParams, signal?: AbortSignal) {
|
||||
const q = new URLSearchParams()
|
||||
if (params.page !== undefined) q.set('page', String(params.page))
|
||||
if (params.pageSize !== undefined) q.set('pageSize', String(params.pageSize))
|
||||
if (params.name) q.set('name', params.name)
|
||||
if (params.domain) q.set('domain', params.domain)
|
||||
if (params.category) q.set('category', params.category)
|
||||
|
||||
const query = q.toString()
|
||||
const path = `/businesses${query ? `?${query}` : ''}`
|
||||
return apiRequest<BusinessesListResponse>(path, { auth: true, signal })
|
||||
}
|
||||
|
||||
export async function createBusiness(payload: CreateBusinessPayload) {
|
||||
return apiRequest<BusinessListItem>('/businesses', {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateBusiness(businessId: string, payload: UpdateBusinessPayload) {
|
||||
return apiRequest<BusinessListItem>(`/businesses/${businessId}`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function addBusinessDomain(businessId: string, payload: AddDomainPayload) {
|
||||
return apiRequest(`/businesses/${businessId}/domains`, {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateBusinessDomain(
|
||||
businessId: string,
|
||||
domainId: number,
|
||||
payload: UpdateDomainPayload,
|
||||
) {
|
||||
return apiRequest(`/businesses/${businessId}/domains/${domainId}`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function setBusinessActive(businessId: string, payload: DisableBusinessPayload) {
|
||||
return apiRequest(`/businesses/${businessId}/disable`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function removeBusiness(businessId: string) {
|
||||
return apiRequest(`/businesses/${businessId}`, {
|
||||
method: 'DELETE',
|
||||
auth: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import type { BusinessPrimaryColorId } from '../utils/businessPrimaryColors'
|
||||
|
||||
export interface BrandingSettings {
|
||||
primaryColor: BusinessPrimaryColorId
|
||||
}
|
||||
|
||||
export interface BusinessSettings {
|
||||
branding: BrandingSettings
|
||||
dashboard: {
|
||||
comments: { autoApprove: boolean }
|
||||
expertReviews: { autoApprove: boolean }
|
||||
}
|
||||
store: {
|
||||
onlineSellEnabled: boolean
|
||||
orderProcessSteps: Array<{ id: string; label: string; color: string }>
|
||||
}
|
||||
}
|
||||
|
||||
export interface BusinessSettingsResponse {
|
||||
businessId: string
|
||||
settings: BusinessSettings
|
||||
}
|
||||
|
||||
export async function getBusinessSettings(businessId: string, signal?: AbortSignal) {
|
||||
return apiRequest<BusinessSettingsResponse>(`/businesses/${businessId}/settings`, {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateBusinessPrimaryColor(
|
||||
businessId: string,
|
||||
primaryColor: BusinessPrimaryColorId,
|
||||
) {
|
||||
return apiRequest<BusinessSettingsResponse>(`/businesses/${businessId}/settings`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: {
|
||||
branding: { primaryColor },
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import type { BusinessCategoriesResponse } from '../types/category'
|
||||
|
||||
export async function listBusinessCategories(signal?: AbortSignal) {
|
||||
return apiRequest<BusinessCategoriesResponse>('/business-categories', { auth: true, signal })
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import type { DomainsListResponse } from '../types/domain'
|
||||
|
||||
export interface ListDomainsParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface UpdateDomainPayload {
|
||||
host?: string
|
||||
expiresAt?: string
|
||||
}
|
||||
|
||||
export async function listDomains(params: ListDomainsParams, signal?: AbortSignal) {
|
||||
const q = new URLSearchParams()
|
||||
if (params.page !== undefined) q.set('page', String(params.page))
|
||||
if (params.pageSize !== undefined) q.set('pageSize', String(params.pageSize))
|
||||
if (params.name) q.set('name', params.name)
|
||||
|
||||
const query = q.toString()
|
||||
const path = `/domains${query ? `?${query}` : ''}`
|
||||
return apiRequest<DomainsListResponse>(path, { auth: true, signal })
|
||||
}
|
||||
|
||||
export async function updateDomain(domainId: number | string, payload: UpdateDomainPayload) {
|
||||
return apiRequest(`/domains/${domainId}`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function setDomainActive(domainId: number | string, isActive: boolean) {
|
||||
return apiRequest(`/domains/${domainId}/disable`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: { isActive },
|
||||
})
|
||||
}
|
||||
|
||||
export async function setDomainSsl(domainId: number | string, sslEnabled: boolean) {
|
||||
return apiRequest(`/domains/${domainId}/ssl`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: { sslEnabled },
|
||||
})
|
||||
}
|
||||
|
||||
export async function removeDomain(domainId: number | string) {
|
||||
return apiRequest(`/domains/${domainId}`, {
|
||||
method: 'DELETE',
|
||||
auth: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import type { AuthUser, MeResponse } from '../types/auth'
|
||||
import type { ProfileFormData } from '../types/profile'
|
||||
|
||||
export async function updateProfile(data: ProfileFormData) {
|
||||
return apiRequest<{ message: string; user: AuthUser }>('/auth/profile', {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
email: data.email || null,
|
||||
about: data.about,
|
||||
city: data.city,
|
||||
address: data.address,
|
||||
landline: data.landline,
|
||||
postalCode: data.postalCode,
|
||||
instagram: data.instagram,
|
||||
telegramId: data.telegramId,
|
||||
linkedin: data.linkedin,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function changePassword(currentPassword: string, newPassword: string) {
|
||||
return apiRequest<{ message: string }>('/auth/change-password', {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: { currentPassword, newPassword },
|
||||
})
|
||||
}
|
||||
|
||||
export async function fetchProfile() {
|
||||
return apiRequest<MeResponse>('/auth/me', { auth: true })
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import type { RolesListResponse } from '../types/user'
|
||||
|
||||
export interface AddTeamMemberPayload {
|
||||
cellNumber: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
roleSlug: string
|
||||
email?: string
|
||||
password?: string
|
||||
}
|
||||
|
||||
export async function listTeamRoles(signal?: AbortSignal) {
|
||||
return apiRequest<RolesListResponse>('/roles?scope=team', { auth: true, signal })
|
||||
}
|
||||
|
||||
export async function addTeamMember(businessId: number | string, payload: AddTeamMemberPayload) {
|
||||
return apiRequest(`/businesses/${businessId}/team`, {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateTeamMemberRole(
|
||||
businessId: number | string,
|
||||
memberId: number | string,
|
||||
roleSlug: string,
|
||||
) {
|
||||
return apiRequest(`/businesses/${businessId}/team/${memberId}`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: { roleSlug },
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import type { RolesListResponse, UserSearchResponse, UsersListResponse } from '../types/user'
|
||||
|
||||
export interface ListUsersParams {
|
||||
page?: number
|
||||
pageSize?: number
|
||||
name?: string
|
||||
cellNumber?: string
|
||||
role?: string
|
||||
businessId?: number
|
||||
membership?: 'staff' | 'all'
|
||||
}
|
||||
|
||||
export interface UpdateUserPayload {
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
email?: string
|
||||
cellNumber?: string
|
||||
}
|
||||
|
||||
export interface CreateUserPayload {
|
||||
cellNumber: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
businessId: number
|
||||
password?: string
|
||||
email?: string
|
||||
}
|
||||
|
||||
export async function searchUsers(q: string, limit = 20, signal?: AbortSignal) {
|
||||
const params = new URLSearchParams({ q, limit: String(limit) })
|
||||
return apiRequest<UserSearchResponse>(`/users/search?${params.toString()}`, {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export async function listUsers(params: ListUsersParams, signal?: AbortSignal) {
|
||||
const q = new URLSearchParams()
|
||||
if (params.page !== undefined) q.set('page', String(params.page))
|
||||
if (params.pageSize !== undefined) q.set('pageSize', String(params.pageSize))
|
||||
if (params.name) q.set('name', params.name)
|
||||
if (params.cellNumber) q.set('cellNumber', params.cellNumber)
|
||||
if (params.role) q.set('role', params.role)
|
||||
if (params.businessId !== undefined) q.set('businessId', String(params.businessId))
|
||||
if (params.membership) q.set('membership', params.membership)
|
||||
|
||||
const query = q.toString()
|
||||
const path = `/users${query ? `?${query}` : ''}`
|
||||
return apiRequest<UsersListResponse>(path, { auth: true, signal })
|
||||
}
|
||||
|
||||
export async function listUserRoles(signal?: AbortSignal) {
|
||||
return apiRequest<RolesListResponse>('/roles?scope=global', { auth: true, signal })
|
||||
}
|
||||
|
||||
export async function updateUserRole(userId: number | string, roleSlug: string) {
|
||||
return apiRequest<{ id: number; role: string; roles: string[] }>(`/users/${userId}/role`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: { roleSlug },
|
||||
})
|
||||
}
|
||||
|
||||
export async function createUser(payload: CreateUserPayload) {
|
||||
return apiRequest(`/users`, {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function updateUser(userId: number | string, payload: UpdateUserPayload) {
|
||||
return apiRequest(`/users/${userId}`, {
|
||||
method: 'PATCH',
|
||||
auth: true,
|
||||
body: payload,
|
||||
})
|
||||
}
|
||||
|
||||
export async function adminResetUserPassword(userId: number | string, newPassword: string) {
|
||||
return apiRequest(`/users/${userId}/reset-password`, {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: { newPassword },
|
||||
})
|
||||
}
|
||||
|
||||
export async function removeUser(userId: number | string) {
|
||||
return apiRequest(`/users/${userId}`, {
|
||||
method: 'DELETE',
|
||||
auth: true,
|
||||
})
|
||||
}
|
||||
|
||||
export async function sendUserMessage(userId: number | string, message: string) {
|
||||
return apiRequest<{ enabled: boolean; message: string }>(`/users/${userId}/send-message`, {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: { message },
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user