Skip to content

Commit 8517bc3

Browse files
chore: wip
1 parent 5bd9f70 commit 8517bc3

34 files changed

+237
-107
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import type { CustomerTable } from '../types'
2+
import { db } from '@stacksjs/database'
3+
4+
export interface FetchCustomersOptions {
5+
page?: number
6+
limit?: number
7+
search?: string
8+
status?: string
9+
sortBy?: string
10+
sortOrder?: 'asc' | 'desc'
11+
}
12+
13+
export interface PaginatedCustomers {
14+
customers: CustomerTable[]
15+
pagination: {
16+
total: number
17+
currentPage: number
18+
totalPages: number
19+
limit: number
20+
hasNextPage: boolean
21+
hasPrevPage: boolean
22+
}
23+
}
24+
25+
export interface CustomerStats {
26+
total: number
27+
active: number
28+
inactive: number
29+
topSpenders: Partial<CustomerTable>[]
30+
recentCustomers: Partial<CustomerTable>[]
31+
}
32+
33+
/**
34+
* Fetch all customers from the database
35+
*/
36+
export async function fetchAll(): Promise<CustomerTable[]> {
37+
return await db
38+
.selectFrom('customers')
39+
.selectAll()
40+
.execute()
41+
}
42+
43+
/**
44+
* Fetch customers with pagination, sorting, and filtering options
45+
*/
46+
export async function fetchPaginated(options: FetchCustomersOptions = {}): Promise<PaginatedCustomers> {
47+
// Set default values
48+
const page = options.page || 1
49+
const limit = options.limit || 10
50+
const offset = (page - 1) * limit
51+
const sortBy = options.sortBy || 'created_at'
52+
const sortOrder = options.sortOrder || 'desc'
53+
54+
// Start building the query
55+
let query = db.selectFrom('customers')
56+
let countQuery = db.selectFrom('customers')
57+
58+
// Apply search filter if provided
59+
if (options.search) {
60+
const searchTerm = `%${options.search}%`
61+
const searchFilter = eb => eb.or([
62+
eb('name', 'like', searchTerm),
63+
eb('email', 'like', searchTerm),
64+
eb('phone', 'like', searchTerm),
65+
])
66+
67+
query = query.where(searchFilter)
68+
countQuery = countQuery.where(searchFilter)
69+
}
70+
71+
// Apply status filter if provided and not 'all'
72+
if (options.status && options.status !== 'all') {
73+
query = query.where('status', '=', options.status)
74+
countQuery = countQuery.where('status', '=', options.status)
75+
}
76+
77+
// Get total count for pagination
78+
const countResult = await countQuery
79+
.select(eb => eb.fn.count('id').as('total'))
80+
.executeTakeFirst()
81+
82+
const total = Number(countResult?.total || 0)
83+
84+
// Apply sorting
85+
// Note: Ensure the column is valid to prevent SQL injection
86+
const validColumns = ['name', 'email', 'orders', 'totalSpent', 'lastOrder', 'status', 'created_at', 'updated_at']
87+
const validSortBy = validColumns.includes(sortBy) ? sortBy : 'created_at'
88+
89+
query = query.orderBy(validSortBy, sortOrder)
90+
91+
// Apply pagination
92+
query = query.limit(limit).offset(offset)
93+
94+
// Execute the query
95+
const customers = await query.selectAll().execute()
96+
97+
// Calculate pagination info
98+
const totalPages = Math.ceil(total / limit)
99+
100+
return {
101+
customers,
102+
pagination: {
103+
total,
104+
currentPage: page,
105+
totalPages,
106+
limit,
107+
hasNextPage: page < totalPages,
108+
hasPrevPage: page > 1,
109+
},
110+
}
111+
}
112+
113+
/**
114+
* Fetch a customer by ID
115+
*/
116+
export async function fetchById(id: number): Promise<CustomerTable | undefined> {
117+
return await db
118+
.selectFrom('customers')
119+
.where('id', '=', id)
120+
.selectAll()
121+
.executeTakeFirst()
122+
}
123+
124+
/**
125+
* Get customer statistics
126+
*/
127+
export async function fetchStats(): Promise<CustomerStats> {
128+
const totalCustomers = await db
129+
.selectFrom('customers')
130+
.select(eb => eb.fn.count('id').as('count'))
131+
.executeTakeFirst()
132+
133+
const activeCustomers = await db
134+
.selectFrom('customers')
135+
.where('status', '=', 'Active')
136+
.select(eb => eb.fn.count('id').as('count'))
137+
.executeTakeFirst()
138+
139+
const inactiveCustomers = await db
140+
.selectFrom('customers')
141+
.where('status', '=', 'Inactive')
142+
.select(eb => eb.fn.count('id').as('count'))
143+
.executeTakeFirst()
144+
145+
const topSpenders = await db
146+
.selectFrom('customers')
147+
.select(['id', 'name', 'email', 'totalSpent'])
148+
.orderBy('totalSpent', 'desc')
149+
.limit(5)
150+
.execute()
151+
152+
const recentCustomers = await db
153+
.selectFrom('customers')
154+
.select(['id', 'name', 'email', 'created_at'])
155+
.orderBy('created_at', 'desc')
156+
.limit(5)
157+
.execute()
158+
159+
return {
160+
total: Number(totalCustomers?.count || 0),
161+
active: Number(activeCustomers?.count || 0),
162+
inactive: Number(inactiveCustomers?.count || 0),
163+
topSpenders,
164+
recentCustomers,
165+
}
166+
}

storage/framework/core/commerce/src/order.ts

Whitespace-only changes.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Define the customer table structure
2+
export interface CustomerTable {
3+
id: number
4+
name: string
5+
email: string
6+
phone: string
7+
orders: number
8+
totalSpent: number
9+
lastOrder: string
10+
status: 'Active' | 'Inactive'
11+
avatar: string
12+
user_id?: number
13+
created_at?: string
14+
updated_at?: string
15+
uuid?: string
16+
}
17+
18+
// Define the input for creating a customer
19+
export interface CreateCustomerInput {
20+
name: string
21+
email: string
22+
phone: string
23+
orders?: number
24+
totalSpent?: number
25+
lastOrder?: string
26+
status?: 'Active' | 'Inactive'
27+
avatar?: string
28+
user_id?: number
29+
}
30+
31+
// Define the input for updating a customer
32+
export interface UpdateCustomerInput {
33+
name?: string
34+
email?: string
35+
phone?: string
36+
orders?: number
37+
totalSpent?: number
38+
lastOrder?: string
39+
status?: 'Active' | 'Inactive'
40+
avatar?: string
41+
user_id?: number
42+
}

storage/framework/core/orm/src/generate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1771,7 +1771,7 @@ export async function generateModelString(
17711771
.execute()
17721772
}
17731773
1774-
applyWhere<V>(column: keyof UsersTable, ...args: [V] | [Operator, V]): UserModel {
1774+
applyWhere<V>(column: keyof ${formattedTableName}Table, ...args: [V] | [Operator, V]): ${modelName}Model {
17751775
if (args.length === 1) {
17761776
const [value] = args
17771777
this.selectFromQuery = this.selectFromQuery.where(column, '=', value)

storage/framework/defaults/models/Customer.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,6 @@ export default {
7575
factory: faker => faker.phone.number(),
7676
},
7777

78-
orders: {
79-
required: false,
80-
default: 0,
81-
order: 4,
82-
fillable: true,
83-
validation: {
84-
rule: schema.number().min(0),
85-
message: {
86-
min: 'Orders count cannot be negative',
87-
},
88-
},
89-
factory: faker => faker.number.int({ min: 0, max: 20 }),
90-
},
91-
9278
totalSpent: {
9379
required: false,
9480
default: 0,
@@ -139,12 +125,6 @@ export default {
139125
},
140126
factory: faker => faker.image.avatar(),
141127
},
142-
143-
user_id: {
144-
required: false,
145-
order: 9,
146-
fillable: true,
147-
},
148128
},
149129

150130
get: {

storage/framework/orm/src/models/AccessToken.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,7 @@ export class AccessTokenModel {
979979
.execute()
980980
}
981981

982-
applyWhere<V>(column: keyof UsersTable, ...args: [V] | [Operator, V]): UserModel {
982+
applyWhere<V>(column: keyof PersonalAccessTokensTable, ...args: [V] | [Operator, V]): AccessTokenModel {
983983
if (args.length === 1) {
984984
const [value] = args
985985
this.selectFromQuery = this.selectFromQuery.where(column, '=', value)

storage/framework/orm/src/models/Coupon.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,7 @@ export class CouponModel {
10361036
.execute()
10371037
}
10381038

1039-
applyWhere<V>(column: keyof UsersTable, ...args: [V] | [Operator, V]): UserModel {
1039+
applyWhere<V>(column: keyof CouponsTable, ...args: [V] | [Operator, V]): CouponModel {
10401040
if (args.length === 1) {
10411041
const [value] = args
10421042
this.selectFromQuery = this.selectFromQuery.where(column, '=', value)

0 commit comments

Comments
 (0)