Skip to content

Commit fc79dff

Browse files
committed
Merge branch 'main' into remove-sendNewsletter
2 parents 079f3e8 + dd334e2 commit fc79dff

File tree

18 files changed

+456
-382
lines changed

18 files changed

+456
-382
lines changed

opensaas-sh/app_diff/src/admin/dashboards/users/UsersTable.tsx.diff

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
--- template/app/src/admin/dashboards/users/UsersTable.tsx
22
+++ opensaas-sh/app/src/admin/dashboards/users/UsersTable.tsx
3-
@@ -9,6 +9,7 @@
4-
const [skip, setskip] = useState(0);
5-
const [page, setPage] = useState(1);
6-
const [email, setEmail] = useState<string | undefined>(undefined);
7-
+
8-
const [isAdminFilter, setIsAdminFilter] = useState<boolean | undefined>(undefined);
9-
const [statusOptions, setStatusOptions] = useState<SubscriptionStatus[]>([]);
10-
const { data, isLoading, error } = useQuery(getPaginatedUsers, {
11-
@@ -211,7 +212,7 @@
3+
@@ -207,7 +207,7 @@
124
<p className='text-sm text-black dark:text-white'>{user.subscriptionStatus}</p>
135
</div>
146
<div className='col-span-2 flex items-center'>

opensaas-sh/app_diff/src/analytics/stats.ts.diff

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
--- template/app/src/analytics/stats.ts
22
+++ opensaas-sh/app/src/analytics/stats.ts
3-
@@ -2,10 +2,8 @@
3+
@@ -2,11 +2,9 @@
44
import { type DailyStatsJob } from 'wasp/server/jobs';
55
import Stripe from 'stripe';
6-
import { stripe } from '../payment/stripe/stripeClient'
6+
import { stripe } from '../payment/stripe/stripeClient';
77
-import { listOrders } from '@lemonsqueezy/lemonsqueezy.js';
88
import { getDailyPageViews, getSources } from './providers/plausibleAnalyticsUtils';
99
-// import { getDailyPageViews, getSources } from './providers/googleAnalyticsUtils';
1010
-import { paymentProcessor } from '../payment/paymentProcessor';
11-
+// import { getDailyPageViews, getSources } from './providers/googleAnalyticsUtils;
11+
import { SubscriptionStatus } from '../payment/plans';
12+
+// import { getDailyPageViews, getSources } from './providers/googleAnalyticsUtils';
1213

1314
export type DailyStatsProps = { dailyStats?: DailyStats; weeklyStats?: DailyStats[]; isLoading?: boolean };
1415

15-
@@ -41,17 +39,7 @@
16+
@@ -42,17 +40,7 @@
1617
paidUserDelta -= yesterdaysStats.paidUserCount;
1718
}
1819

@@ -27,11 +28,11 @@
2728
- default:
2829
- throw new Error(`Unsupported payment processor: ${paymentProcessor.id}`);
2930
- }
30-
+ let totalRevenue = await fetchTotalStripeRevenue()
31+
+ let totalRevenue = await fetchTotalStripeRevenue();
3132

3233
const { totalViews, prevDayViewsChangePercent } = await getDailyPageViews();
3334

34-
@@ -162,38 +150,3 @@
35+
@@ -163,38 +151,3 @@
3536
// Revenue is in cents so we convert to dollars (or your main currency unit)
3637
return totalRevenue / 100;
3738
}
@@ -70,4 +71,3 @@
7071
- throw error;
7172
- }
7273
-}
73-
\ No newline at end of file
Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,31 @@
11
--- template/app/src/user/operations.ts
22
+++ opensaas-sh/app/src/user/operations.ts
3-
@@ -41,7 +41,10 @@
3+
@@ -34,10 +34,7 @@
44
};
55

66
type GetPaginatedUsersOutput = {
7-
- users: Pick<User, 'id' | 'email' | 'username' | 'subscriptionStatus' | 'paymentProcessorUserId'>[];
8-
+ users: Pick<
9-
+ User,
10-
+ 'id' | 'email' | 'username' | 'subscriptionStatus' | 'stripeId'
11-
+ >[];
7+
- users: Pick<
8+
- User,
9+
- 'id' | 'email' | 'username' | 'subscriptionStatus' | 'paymentProcessorUserId' | 'isAdmin'
10+
- >[];
11+
+ users: Pick<User, 'id' | 'email' | 'username' | 'subscriptionStatus' | 'stripeId' | 'isAdmin'>[];
1212
totalPages: number;
1313
};
1414

15-
@@ -85,6 +88,7 @@
15+
@@ -85,6 +82,7 @@
1616
mode: 'insensitive',
1717
},
1818
isAdmin,
1919
+ isMockUser: true,
2020
},
2121
{
2222
OR: [
23-
@@ -108,7 +112,7 @@
23+
@@ -106,7 +104,7 @@
2424
username: true,
2525
isAdmin: true,
2626
subscriptionStatus: true,
2727
- paymentProcessorUserId: true,
2828
+ stripeId: true,
2929
},
3030
orderBy: {
31-
id: 'desc',
32-
@@ -124,6 +128,7 @@
33-
mode: 'insensitive',
34-
},
35-
isAdmin,
36-
+ isMockUser: true,
37-
},
38-
{
39-
OR: [
31+
username: 'asc',

template/app/schema.prisma

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,4 @@ model ContactFormMessage {
109109
content String
110110
isRead Boolean @default(false)
111111
repliedAt DateTime?
112-
}
112+
}

template/app/src/admin/dashboards/users/SwitcherOne.tsx

Lines changed: 0 additions & 33 deletions
This file was deleted.

template/app/src/admin/dashboards/users/UsersTable.tsx

Lines changed: 96 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
1-
import { type SubscriptionStatus } from '../../../payment/plans';
2-
import { updateIsUserAdminById, useQuery, getPaginatedUsers } from 'wasp/client/operations';
1+
import { SubscriptionStatus } from '../../../payment/plans';
2+
import { useQuery, getPaginatedUsers } from 'wasp/client/operations';
33
import { useState, useEffect } from 'react';
4-
import SwitcherOne from './SwitcherOne';
4+
import SwitcherOne from '../../elements/forms/SwitcherOne';
55
import LoadingSpinner from '../../layout/LoadingSpinner';
66
import DropdownEditDelete from './DropdownEditDelete';
7+
import { updateIsUserAdminById } from 'wasp/client/operations';
8+
import { type User } from 'wasp/entities';
9+
10+
function AdminSwitch({ id, isAdmin }: Pick<User, 'id' | 'isAdmin'>) {
11+
return (
12+
<SwitcherOne isOn={isAdmin} onChange={(value) => updateIsUserAdminById({ id: id, isAdmin: value })} />
13+
);
14+
}
715

816
const UsersTable = () => {
9-
const [skip, setskip] = useState(0);
10-
const [page, setPage] = useState(1);
11-
const [email, setEmail] = useState<string | undefined>(undefined);
17+
const [currentPage, setCurrentPage] = useState(1);
18+
const [emailFilter, setEmailFilter] = useState<string | undefined>(undefined);
1219
const [isAdminFilter, setIsAdminFilter] = useState<boolean | undefined>(undefined);
13-
const [statusOptions, setStatusOptions] = useState<SubscriptionStatus[]>([]);
14-
const { data, isLoading, error } = useQuery(getPaginatedUsers, {
15-
skip,
16-
emailContains: email,
17-
isAdmin: isAdminFilter,
18-
subscriptionStatus: statusOptions?.length > 0 ? statusOptions : undefined,
19-
});
20+
const [subscriptionStatusFilter, setSubcriptionStatusFilter] = useState<Array<SubscriptionStatus | null>>(
21+
[]
22+
);
2023

21-
useEffect(() => {
22-
setPage(1);
23-
}, [email, statusOptions]);
24+
const skipPages = currentPage - 1;
2425

25-
useEffect(() => {
26-
setskip((page - 1) * 10);
27-
}, [page]);
26+
const { data, isLoading } = useQuery(getPaginatedUsers, {
27+
skipPages,
28+
filter: {
29+
...(emailFilter && { emailContains: emailFilter }),
30+
...(isAdminFilter !== undefined && { isAdmin: isAdminFilter }),
31+
...(subscriptionStatusFilter.length > 0 && { subscriptionStatusIn: subscriptionStatusFilter }),
32+
},
33+
});
34+
35+
useEffect(
36+
function backToPageOne() {
37+
setCurrentPage(1);
38+
},
39+
[emailFilter, subscriptionStatusFilter, isAdminFilter]
40+
);
2841

2942
return (
3043
<div className='flex flex-col gap-4'>
@@ -41,7 +54,8 @@ const UsersTable = () => {
4154
id='email-filter'
4255
placeholder='[email protected]'
4356
onChange={(e) => {
44-
setEmail(e.currentTarget.value);
57+
const value = e.currentTarget.value;
58+
setEmailFilter(value === '' ? undefined : value);
4559
}}
4660
className='rounded border border-stroke py-2 px-5 bg-white outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary'
4761
/>
@@ -50,8 +64,8 @@ const UsersTable = () => {
5064
</label>
5165
<div className='flex-grow relative z-20 rounded border border-stroke pr-8 outline-none bg-white transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input'>
5266
<div className='flex items-center'>
53-
{!!statusOptions && statusOptions.length > 0 ? (
54-
statusOptions.map((opt, idx) => (
67+
{subscriptionStatusFilter.length > 0 ? (
68+
subscriptionStatusFilter.map((opt) => (
5569
<span
5670
key={opt}
5771
className='z-30 flex items-center my-1 mx-2 py-1 px-2 outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary'
@@ -60,26 +74,13 @@ const UsersTable = () => {
6074
<span
6175
onClick={(e) => {
6276
e.stopPropagation();
63-
setStatusOptions((prevValue) => {
77+
setSubcriptionStatusFilter((prevValue) => {
6478
return prevValue?.filter((val) => val !== opt);
6579
});
6680
}}
6781
className='z-30 cursor-pointer pl-2 hover:text-danger'
6882
>
69-
<svg
70-
width='14'
71-
height='14'
72-
viewBox='0 0 12 12'
73-
fill='none'
74-
xmlns='http://www.w3.org/2000/svg'
75-
>
76-
<path
77-
fillRule='evenodd'
78-
clipRule='evenodd'
79-
d='M9.35355 3.35355C9.54882 3.15829 9.54882 2.84171 9.35355 2.64645C9.15829 2.45118 8.84171 2.45118 8.64645 2.64645L6 5.29289L3.35355 2.64645C3.15829 2.45118 2.84171 2.45118 2.64645 2.64645C2.45118 2.84171 2.45118 3.15829 2.64645 3.35355L5.29289 6L2.64645 8.64645C2.45118 8.84171 2.45118 9.15829 2.64645 9.35355C2.84171 9.54882 3.15829 9.54882 3.35355 9.35355L6 6.70711L8.64645 9.35355C8.84171 9.54882 9.15829 9.54882 9.35355 9.35355C9.54882 9.15829 9.54882 8.84171 9.35355 8.64645L6.70711 6L9.35355 3.35355Z'
80-
fill='currentColor'
81-
></path>
82-
</svg>
83+
<XIcon />
8384
</span>
8485
</span>
8586
))
@@ -91,45 +92,37 @@ const UsersTable = () => {
9192
</div>
9293
<select
9394
onChange={(e) => {
94-
const targetValue = e.target.value === '' ? null : e.target.value;
95-
setStatusOptions((prevValue) => {
96-
if (prevValue?.includes(targetValue as SubscriptionStatus)) {
97-
return prevValue?.filter((val) => val !== targetValue);
98-
} else if (!!prevValue) {
99-
return [...prevValue, targetValue as SubscriptionStatus];
100-
} else {
101-
return prevValue;
102-
}
103-
});
95+
const selectedValue = e.target.value == 'has_not_subscribed' ? null : e.target.value;
96+
97+
console.log(selectedValue);
98+
if (selectedValue === 'clear-all') {
99+
setSubcriptionStatusFilter([]);
100+
} else {
101+
setSubcriptionStatusFilter((prevValue) => {
102+
if (prevValue.includes(selectedValue as SubscriptionStatus)) {
103+
return prevValue.filter((val) => val !== selectedValue);
104+
} else {
105+
return [...prevValue, selectedValue as SubscriptionStatus];
106+
}
107+
});
108+
}
104109
}}
105110
name='status-filter'
106111
id='status-filter'
107112
className='absolute top-0 left-0 z-20 h-full w-full bg-white opacity-0'
108113
>
109-
<option value=''>Select filters</option>
110-
{['past_due', 'canceled', 'active', 'deleted', null].map((status) => {
111-
if (!statusOptions.includes(status as SubscriptionStatus)) {
112-
return <option value={status || ''}>{status ? status : 'has not subscribed'}</option>;
113-
}
114-
})}
114+
<option value='select-filters'>Select filters</option>
115+
{[...Object.values(SubscriptionStatus), null]
116+
.filter((status) => !subscriptionStatusFilter.includes(status))
117+
.map((status) => {
118+
const extendedStatus = status ?? 'has_not_subscribed'
119+
return <option key={extendedStatus} value={extendedStatus}>
120+
{extendedStatus}
121+
</option>
122+
})}
115123
</select>
116124
<span className='absolute top-1/2 right-4 z-10 -translate-y-1/2'>
117-
<svg
118-
width='24'
119-
height='24'
120-
viewBox='0 0 24 24'
121-
fill='none'
122-
xmlns='http://www.w3.org/2000/svg'
123-
>
124-
<g opacity='0.8'>
125-
<path
126-
fillRule='evenodd'
127-
clipRule='evenodd'
128-
d='M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z'
129-
fill='#637381'
130-
></path>
131-
</g>
132-
</svg>
125+
<ChevronDownIcon />
133126
</span>
134127
</div>
135128
<div className='flex items-center gap-2'>
@@ -158,11 +151,14 @@ const UsersTable = () => {
158151
<span className='text-md mr-2 text-black dark:text-white'>page</span>
159152
<input
160153
type='number'
161-
value={page}
162154
min={1}
155+
defaultValue={currentPage}
163156
max={data?.totalPages}
164157
onChange={(e) => {
165-
setPage(parseInt(e.currentTarget.value));
158+
const value = parseInt(e.currentTarget.value);
159+
if (data?.totalPages && value <= data?.totalPages && value > 0) {
160+
setCurrentPage(value);
161+
}
166162
}}
167163
className='rounded-md border-1 border-stroke bg-transparent px-4 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary'
168164
/>
@@ -215,7 +211,7 @@ const UsersTable = () => {
215211
</div>
216212
<div className='col-span-1 flex items-center'>
217213
<div className='text-sm text-black dark:text-white'>
218-
<SwitcherOne user={user} updateIsUserAdminById={updateIsUserAdminById} />
214+
<AdminSwitch {...user} />
219215
</div>
220216
</div>
221217
<div className='col-span-1 flex items-center'>
@@ -228,4 +224,32 @@ const UsersTable = () => {
228224
);
229225
};
230226

227+
function ChevronDownIcon() {
228+
return (
229+
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
230+
<g opacity='0.8'>
231+
<path
232+
fillRule='evenodd'
233+
clipRule='evenodd'
234+
d='M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z'
235+
fill='#637381'
236+
></path>
237+
</g>
238+
</svg>
239+
);
240+
}
241+
242+
function XIcon() {
243+
return (
244+
<svg width='14' height='14' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'>
245+
<path
246+
fillRule='evenodd'
247+
clipRule='evenodd'
248+
d='M9.35355 3.35355C9.54882 3.15829 9.54882 2.84171 9.35355 2.64645C9.15829 2.45118 8.84171 2.45118 8.64645 2.64645L6 5.29289L3.35355 2.64645C3.15829 2.45118 2.84171 2.45118 2.64645 2.64645C2.45118 2.84171 2.45118 3.15829 2.64645 3.35355L5.29289 6L2.64645 8.64645C2.45118 8.84171 2.45118 9.15829 2.64645 9.35355C2.84171 9.54882 3.15829 9.54882 3.35355 9.35355L6 6.70711L8.64645 9.35355C8.84171 9.54882 9.15829 9.54882 9.35355 9.35355C9.54882 9.15829 9.54882 8.84171 9.35355 8.64645L6.70711 6L9.35355 3.35355Z'
249+
fill='currentColor'
250+
></path>
251+
</svg>
252+
);
253+
}
254+
231255
export default UsersTable;

0 commit comments

Comments
 (0)