reorganize admin dash (#227)

* reorganize admin dash

* remove messages & ui stuff from admin

* separate analytics from admin

* update app_diff

* Update .env.vault.diff
This commit is contained in:
vincanger
2024-07-15 11:10:17 +02:00
committed by GitHub
parent a812b2d7ac
commit f9c920f0f1
107 changed files with 855 additions and 968 deletions

View File

@@ -7,8 +7,8 @@
+#/--------------------------------------------------/
+
+# development
+DOTENV_VAULT_DEVELOPMENT="rIWic4/sAo8dJssxICxAqiZ8NBoA22gHzUI8k/osrn1NgPlAbn7PG6VB//SA2OHByNii1hrnjjcXgRma6x0FESvR7FGlvpA3we6oQY6QL1vqgPsqSpEURL/NuOAAUAisC05idBnOecb4D9jc9/4CVR3+dhzOcprZvFNoBVdHpz7kKzzCjU+iKKfHIzl2IpzcNyu7z+1TkafJqFeJoGfO4F36WfybSTsBJydq6xEMimQiEUzatl1dRXfTRbGxMzWBPgRi/Xyqe71p2jRHpVLFQZA0qC4xmu5y+NY2y1q47ytyjlZS9eAvUxuXNbvnWci/sfKFxZt6Ve8C2v+XaVAA2OSMrHTlTRXBeJASaDUIfdK1wT+zK+D0BZnC5y8TVD/abLfOGdkNPbRWZDBWeKYgWQlBYoxoktavpLdLujN9QH3F7dMK5qVZzRGjMZafNmCRkmrOq6ezQGM52yGUKipOvTbBr48ZzoIK1Z2caeG6xfeYuCYxuhWmshOq2Gn0Z85cVnWgKlmZmqJLKKAVE3FswEbSxSnerR+wqghYuu7RCLOrSvAIf05ieHWL2tS8bks1DU4ycqrnSAxlRDAUhyVXAN+o2MNkHNGVIEspSNoNl7M72d7hv9ZFBXE3CK76K8WEl0qRA2RPKVe4KwOU2GO8xgvRN21Q3U1EBwDIXAPgThojZc+eLVPKcjYuGBzijuGmDacmJw/Ok7EFsQip9eUNWG8Oa/BbALmmP6c82jHcNY+9ZiulsVY77FAKiIvRWL5man9FNK3F6yj4hu69S2SEQjNOORosR7zIkz91UTk5Cp/dbGUi0naGVqQNGE3EDEv4cN56VtWoMbwFy5roiIni65oPjQROP+DFjk7uMjnTXAGt237QoOFnCyyjlp9YPHJB+qNS4NwjZMFMXlrY0WUZPs+x04i5OfenqU+ENFJY+GWDYis5m4ex9focjdocxBoNgBPWTgW7NCGMF5rLCP8n42GItRXqWHlnouakDJCy2Wwiqcz0hK1WsUSyOM6rITMJ6ErHtfPT1337rHObnQPwe2keExx6FNkJz65ARDOb+dhK46aQCVk4BX4q7/4a76bRYxrrsaZGmThPeiA0sjJClVAMHqLSeioWqOB2z63o1PHHHSApzsywPE7I9zH4MT1/dkeFRw6YioMiEC+FEje6wN/liCDEs94F1M6T+sD1dbMQQqIoS1Frre2aXfIkvb2VxzdFNsBXX+XmME6wUDGjunUUbCW4W+wvZk43gAXwX3WnzZmss5LQbgAnUTqGD4aKZgf9miba0YaAooXtupDsWx+taVn+IWumu9FOJgjUXk8+v1iiqQpXcSNRHeJi2A3dvHvEDPapcQNkruFQWo4Vu4jvvdLgk+Qtfug9cbqBULKCVqZPrAHShFDmi3kjyIEcS46JiLH+HHu6jutrYYRx7xktYXz6XD14qjsbwi4jQCHJ+TSYGZ9vlJa0zDd+GDDA5SHEuuzHlWLocwfmzIkXLyFvLDN/Tn6hHSxMAg4QesmAONxuFaYPt0rB5zemkrHQD6++M/0P6PElboWuWbN2veOIJtDpUPzsLSlLYBaeNonRk+RevKgjwqGURzB7lYkGtosSV2Pyd149u9qN2TH79SypvodlEzruq4CV3CqBqlFmOnkZyVaSe0AjQeJ/g/Vgj4XWKFnQyHgUcE3mV2oV6gVw5f5OToot0XmeyQdjY8T6g17V3T3ZJDQ9WACFc1lXGTqQdXWGh/Pu7o29RvPg7BPhVYyQEmNgmE223khwV668Vv6sahSovMbHPw0SZof+M/5zOmZTMRp8pWzJGsnscFMv8etefBcPKf1lXMnIk0xDX77Ma3rZlljIWJOe/LYpl76Cp3OdnCWYxI/j7dqLsN/sQYgbp8vC0p3b7bO5vISwu6mPkIDz2sDFUppWAnBpk5eilTl4uKUswW0RRmO0qixIo+MCXo2RwwXJhMxUkPNLpKRfgY4thZPwEu5X2JFKKnqv3sz89de0awRmBmE142KUbYZQK1JCh0oyRVFeAEmB8wMpSU+5UNXSSRnaDJfFIt1hPZ3hkv9Lf5lImeuabZOWZlE4zHTSu7pmyv+welMf/fXYHmLuFS64ZjyTuJ4SBN5ygkfv1gOqf2KqSuke8zdVFHGlZ8Tlww=="
+DOTENV_VAULT_DEVELOPMENT_VERSION=5
+DOTENV_VAULT_DEVELOPMENT="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"
+DOTENV_VAULT_DEVELOPMENT_VERSION=6
+
+# ci
+DOTENV_VAULT_CI="uIKC5RKdFeJiCAzoQ9e2oaT2EbR8usJcZIYlSoFl3HTFrh7J/qfo+Z+OqBgJKQllvCVIK4oQUsLICVWCRs+7yWp5j6gn2yu67lN4lKAfX0QCF/VFYNWa6u1NWZGT1bPRlvNIY7z/YnJZfgaq8Uj+mSU+C14KHMMCpKCSkbqqFX8AWlcPSkbjXaJP4IL3HxzPUGTMfq/+XbokI9r1UjBNYi1yus6jGjI4U2OtiDc61LZJ6Y0tsBeA8NA0iT2S+lwTJBjTmGHoMMjtK08Iw8GAXv9eejETvbHZfFTOpphIjeFXjFRUsgRfuPP3p/lErfncXofQ++9KNS5lhxINGBXQIKh2CRnjhajut38Xxr848YNAj4Ie4RKzeU1+aC6BXONhbD7qZQJmm63vtwTKwrUf8ML7T27DwdpV7dTdz5/W3UWnmbzhfI1pLeCHpBC1FrLBaxhFNFlkqsXiopen4iJf4ihWICn1hMCBGGRosKQJdj+y65FJMwraBbEVHa29FHYruhAPGFqGfEIWvQ++LXlPguJM/RL2txMoVppDanOwFCM3dMO3sF55BEuSSNJWX2s72ZDgTW4/Il7YiIBwwxChPs5vOQweYKqOd21HCl4t+ocu95ja3ZGROnhff72j2pCk7cahaVl/PS8WU7nZ1JQtkv5vNRR3nHgAH1nS9tSBDJ8twKEQ4bJcu2qdhGeQwenKRsV4nrKooGnQ8UlsvoTyozYYCseds2/iXX5vlc/lhL1fgcPPxoss2p+lDaS3AIIEMq0/TUCeRF2ImYUIMnnvCEb0T32Pz8bIfPblSThETdQ5A7ZRjyU9T9Ff6lpxqQwGUWO/ElKZ/CDsoeuj1UqOVLGRBwThrOHlU7LZ6kJnBvcy6AmWccE5fLsmf1l189FwMH6CimHIg2OC4ndCbiRKiP3+3OYHL0JSkTt8Lrsu/b+Mu0Kmiks1A16Sx2Lu1lUfPSsbAgYRYni7OkpgCOJ5DdnP607LTFVIichq4+KzQsc="

View File

@@ -84,7 +84,7 @@
},
},
}
@@ -323,7 +321,10 @@
@@ -195,7 +193,10 @@
email String? @unique
username String? @unique
lastActiveTimestamp DateTime @default(now())

View File

@@ -1,27 +1,23 @@
--- template/app/src/client/admin/pages/DashboardPage.tsx
+++ opensaas-sh/app/src/client/admin/pages/DashboardPage.tsx
@@ -1,5 +1,7 @@
--- template/app/src/admin/dashboards/analytics/AnalyticsDashboardPage.tsx
+++ opensaas-sh/app/src/admin/dashboards/analytics/AnalyticsDashboardPage.tsx
@@ -1,4 +1,5 @@
import { type User } from 'wasp/entities';
import { useQuery, getDailyStats } from 'wasp/client/operations';
+import { Link } from "wasp/client/router";
+import { useState, useEffect } from 'react';
import TotalSignupsCard from '../components/TotalSignupsCard';
import TotalPageViewsCard from '../components/TotalPaidViewsCard';
import TotalPayingUsersCard from '../components/TotalPayingUsersCard';
@@ -10,6 +12,8 @@
import { useQuery, getDailyStats } from 'wasp/client/operations';
import TotalSignupsCard from './TotalSignupsCard';
import TotalPageViewsCard from './TotalPageViewsCard';
@@ -10,6 +11,7 @@
import { useHistory } from 'react-router-dom';
const Dashboard = ({ user }: { user: User }) => {
+ const [isDemoInfoVisible, setIsDemoInfoVisible] = useState(false);
+
const history = useHistory();
if (!user.isAdmin) {
history.push('/');
@@ -17,8 +21,41 @@
@@ -17,8 +19,40 @@
const { data: stats, isLoading, error } = useQuery(getDailyStats);
+
+ useEffect(() => {
+ try {
+ if (localStorage.getItem('isStripeDemoInfoVisible') === 'false') {

View File

@@ -1,19 +1,21 @@
--- template/app/src/client/admin/components/UsersTable.tsx
+++ opensaas-sh/app/src/client/admin/components/UsersTable.tsx
@@ -11,6 +11,7 @@
--- template/app/src/admin/dashboards/users/UsersTable.tsx
+++ opensaas-sh/app/src/admin/dashboards/users/UsersTable.tsx
@@ -9,8 +9,10 @@
const [skip, setskip] = useState(0);
const [page, setPage] = useState(1);
const [email, setEmail] = useState<string | undefined>(undefined);
+
const [isAdminFilter, setIsAdminFilter] = useState<boolean | undefined>(undefined);
const [statusOptions, setStatusOptions] = useState<SubscriptionStatus[]>([]);
+ const [isDemoInfoVisible, setIsDemoInfoVisible] = useState(false);
const { data, isLoading, error } = useQuery(getPaginatedUsers, {
skip,
emailContains: email,
@@ -26,8 +27,43 @@
@@ -26,8 +28,42 @@
setskip((page - 1) * 10);
}, [page]);
+
+ useEffect(() => {
+ useEffect(() => {
+ try {
+ if (localStorage.getItem('isDemoInfoVisible') === 'false') {
+ // do nothing
@@ -25,14 +27,14 @@
+ }
+ }, []);
+
+ const handleDemoInfoClose = () => {
+ try {
+ localStorage.setItem('isDemoInfoVisible', 'false');
+ setIsDemoInfoVisible(false);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+ const handleDemoInfoClose = () => {
+ try {
+ localStorage.setItem('isDemoInfoVisible', 'false');
+ setIsDemoInfoVisible(false);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
return (
<div className='flex flex-col gap-4'>

View File

@@ -24,7 +24,7 @@
import { UserMenuItems } from '../../user/UserMenuItems';
-import { DocsUrl } from '../../common';
+import { DocsUrl, GithubUrl } from '../../common';
import DarkModeSwitcher from '../admin/components/DarkModeSwitcher';
import DarkModeSwitcher from '../components/DarkModeSwitcher';
export default function LandingPage() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);

View File

@@ -20,8 +20,7 @@ entity User {=psl
//...
```
To give yourself administrator priveledges, make sure you add your email addresses to the `ADMIN_EMAILS` environment variable in `.env.server` file before registering/logging in with that email address.
To give yourself administrator priveledges, make sure you add your email addresses to the `ADMIN_EMAILS` environment variable in `.env.server` file before registering/logging in with that email address:
```sh title=".env.server"
ADMIN_EMAILS=me@example.com
@@ -30,13 +29,12 @@ ADMIN_EMAILS=me@example.com
ADMIN_EMAILS=me@example.com,you@example.com,them@example.com
```
if you've already logged in with an email address that you want to give admin priveledges to, you can run the following command in a separate terminal window to update the user's `isAdmin` field:
Or if you've already logged in with an email address that you want to give admin priveledges to, you can run the following command in a separate terminal window to update the user's `isAdmin` field manually:
```sh
wasp db studio
```
![db studio](/stripe/db-studio.png)
---
@@ -49,8 +47,8 @@ If you're finding this template and its guides useful, consider giving us [a sta
## Admin Dashboard Pages
### Dashboard
The Admin dashboard is a single place for you to view your most important metrics and perform some admin tasks. At the moment, it pulls data from:
### Analytics Dashboard
The Admin analytics dashboard is a single place for you to view your most important metrics and perform some admin tasks. At the moment, it pulls data from:
<!-- TODO: add photo -->
@@ -67,7 +65,23 @@ The Admin dashboard is a single place for you to view your most important metric
- total number of paying users
- daily change in number of paying users
For a guide on how to integrate these services, check out the [Stripe](/guides/stripe-integration) and [Analytics guide](/guides/analytics) of the docs.
These metrics are aggregated within the background job `dailyStatsJob`, which by default is run every hour. You can change the frequency of this job by modifying its `cron` field:
```ts title="main.wasp" {8,7}
job dailyStatsJob {
executor: PgBoss,
perform: {
fn: import { calculateDailyStats } from "@src/analytics/stats"
},
schedule: {
cron: "0 * * * *" // every hour. useful in production
// cron: "* * * * *" // every minute. useful for debugging
},
entities: [User, DailyStats, Logs, PageViewSource]
}
```
For a guide on how to integrate these services so that you can view your analytics via the dashboard, check out the [Stripe](/guides/stripe-integration) and [Analytics guide](/guides/analytics) of the docs.
:::note[Help us improve]
We're always looking to improve the Admin dashboard. If you feel something is missing or could be improved, consider [opening an issue](https://github.com/wasp-lang/open-saas/issues) or [submitting a pull request](https://github.com/wasp-lang/open-saas/pulls)
@@ -77,4 +91,4 @@ We're always looking to improve the Admin dashboard. If you feel something is mi
The Users page is where you can view all your users and their most important details. You can also search and filter users by:
- email address
- subscription/payment status
- admin status

View File

@@ -63,7 +63,7 @@ After you sign up for [Google analytics](https://analytics.google.com/), go to y
Once you've completed the steps to create a new Property, some Installation Instructions will pop up. Select `install manually` where you should see a string that looks like this:
```sh "<your-google-analytics-id>"
```sh title="<your-google-analytics-id>"
https://www.googletagmanager.com/gtag/js?id=<your-google-analytics-id>
```
and copy and paste the Google Analytics ID into your `.env.client` file to get it working with the [Cookie Consent Modal](./cookie-consent.md) provided with this template:

View File

@@ -108,55 +108,6 @@ entity Task {=psl
isDone Boolean @default(false)
psl=}
// TODO: add functionality to allow users to send messages to admin
// and make them accessible via the admin dashboard
entity ContactFormMessage {=psl
id String @id @default(uuid())
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
userId String
content String
isRead Boolean @default(false)
repliedAt DateTime?
psl=}
entity DailyStats {=psl
id Int @id @default(autoincrement())
date DateTime @default(now()) @unique
totalViews Int @default(0)
prevDayViewsChangePercent String @default("0")
userCount Int @default(0)
paidUserCount Int @default(0)
userDelta Int @default(0)
paidUserDelta Int @default(0)
totalRevenue Float @default(0)
totalProfit Float @default(0)
sources PageViewSource[]
psl=}
entity PageViewSource {=psl
@@id([date, name])
name String
date DateTime @default(now())
dailyStats DailyStats? @relation(fields: [dailyStatsId], references: [id])
dailyStatsId Int?
visitors Int
psl=}
entity Logs {=psl
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
message String
level String
psl=}
route LandingPageRoute { path: "/", to: LandingPage }
page LandingPage {
component: import LandingPage from "@src/client/landing-page/LandingPage"
@@ -195,68 +146,6 @@ page DemoAppPage {
component: import DemoAppPage from "@src/client/app/DemoAppPage"
}
//#region Admin Pages
route AdminRoute { path: "/admin", to: DashboardPage }
page DashboardPage {
authRequired: true,
component: import Dashboard from "@src/client/admin/pages/DashboardPage"
}
route AdminUsersRoute { path: "/admin/users", to: AdminUsersPage }
page AdminUsersPage {
authRequired: true,
component: import AdminUsers from "@src/client/admin/pages/Users"
}
route AdminSettingsRoute { path: "/admin/settings", to: AdminSettingsPage }
page AdminSettingsPage {
authRequired: true,
component: import AdminSettings from "@src/client/admin/pages/Settings"
}
route AdminChartsRoute { path: "/admin/chart", to: AdminChartsPage }
page AdminChartsPage {
authRequired: true,
component: import AdminCharts from "@src/client/admin/pages/Chart"
}
route AdminMessagesRoute { path: "/admin/messages", to: AdminMessagesPage }
page AdminMessagesPage {
authRequired: true,
component: import AdminMessages from "@src/client/admin/pages/Messages"
}
route AdminFormElementsRoute { path: "/admin/forms/form-elements", to: AdminFormElementsPage }
page AdminFormElementsPage {
authRequired: true,
component: import AdminForms from "@src/client/admin/pages/Form/FormElements"
}
route AdminFormLayoutsRoute { path: "/admin/forms/form-layouts", to: AdminFormLayoutsPage }
page AdminFormLayoutsPage {
authRequired: true,
component: import AdminForms from "@src/client/admin/pages/Form/FormLayout"
}
route AdminCalendarRoute { path: "/admin/calendar", to: AdminCalendarPage }
page AdminCalendarPage {
authRequired: true,
component: import AdminCalendar from "@src/client/admin/pages/Calendar"
}
route AdminUIAlertsRoute { path: "/admin/ui/alerts", to: AdminUIAlertsPage }
page AdminUIAlertsPage {
authRequired: true,
component: import AdminUI from "@src/client/admin/pages/UiElements/Alerts"
}
route AdminUIButtonsRoute { path: "/admin/ui/buttons", to: AdminUIButtonsPage }
page AdminUIButtonsPage {
authRequired: true,
component: import AdminUI from "@src/client/admin/pages/UiElements/Buttons"
}
//#endregion
action generateGptResponse {
fn: import { generateGptResponse } from "@src/server/actions.js",
entities: [User, Task, GptResponse]
@@ -287,11 +176,6 @@ query getAllTasksByUser {
entities: [Task]
}
query getDailyStats {
fn: import { getDailyStats } from "@src/server/queries.js",
entities: [User, DailyStats]
}
job emailChecker {
executor: PgBoss,
perform: {
@@ -303,18 +187,6 @@ job emailChecker {
entities: [User]
}
job dailyStatsJob {
executor: PgBoss,
perform: {
fn: import { calculateDailyStats } from "@src/server/workers/calculateDailyStats.js"
},
schedule: {
cron: "0 * * * *" // every hour. useful in production
// cron: "* * * * *" // every minute. useful for debugging
},
entities: [User, DailyStats, Logs, PageViewSource]
}
//#region User
entity User {=psl
id String @id @default(uuid())
@@ -421,3 +293,136 @@ entity File {=psl
uploadUrl String
psl=}
//#endregion
//#region Analytics
query getDailyStats {
fn: import { getDailyStats } from "@src/analytics/operations",
entities: [User, DailyStats]
}
job dailyStatsJob {
executor: PgBoss,
perform: {
fn: import { calculateDailyStats } from "@src/analytics/stats"
},
schedule: {
cron: "0 * * * *" // every hour. useful in production
// cron: "* * * * *" // every minute. useful for debugging
},
entities: [User, DailyStats, Logs, PageViewSource]
}
entity DailyStats {=psl
id Int @id @default(autoincrement())
date DateTime @default(now()) @unique
totalViews Int @default(0)
prevDayViewsChangePercent String @default("0")
userCount Int @default(0)
paidUserCount Int @default(0)
userDelta Int @default(0)
paidUserDelta Int @default(0)
totalRevenue Float @default(0)
totalProfit Float @default(0)
sources PageViewSource[]
psl=}
entity PageViewSource {=psl
@@id([date, name])
name String
date DateTime @default(now())
dailyStats DailyStats? @relation(fields: [dailyStatsId], references: [id])
dailyStatsId Int?
visitors Int
psl=}
entity Logs {=psl
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
message String
level String
psl=}
//#endregion
//#region Admin Dashboard
route AdminRoute { path: "/admin", to: AnalyticsDashboardPage }
page AnalyticsDashboardPage {
authRequired: true,
component: import AnalyticsDashboardPage from "@src/admin/dashboards/analytics/AnalyticsDashboardPage"
}
route AdminUsersRoute { path: "/admin/users", to: AdminUsersPage }
page AdminUsersPage {
authRequired: true,
component: import AdminUsers from "@src/admin/dashboards/users/UsersDashboardPage"
}
route AdminSettingsRoute { path: "/admin/settings", to: AdminSettingsPage }
page AdminSettingsPage {
authRequired: true,
component: import AdminSettings from "@src/admin/elements/settings/SettingsPage"
}
route AdminChartsRoute { path: "/admin/chart", to: AdminChartsPage }
page AdminChartsPage {
authRequired: true,
component: import AdminCharts from "@src/admin/elements/charts/ChartsPage"
}
route AdminFormElementsRoute { path: "/admin/forms/form-elements", to: AdminFormElementsPage }
page AdminFormElementsPage {
authRequired: true,
component: import AdminForms from "@src/admin/elements/forms/FormElementsPage"
}
route AdminFormLayoutsRoute { path: "/admin/forms/form-layouts", to: AdminFormLayoutsPage }
page AdminFormLayoutsPage {
authRequired: true,
component: import AdminForms from "@src/admin/elements/forms/FormLayoutsPage"
}
route AdminCalendarRoute { path: "/admin/calendar", to: AdminCalendarPage }
page AdminCalendarPage {
authRequired: true,
component: import AdminCalendar from "@src/admin/elements/calendar/CalendarPage"
}
route AdminUIAlertsRoute { path: "/admin/ui/alerts", to: AdminUIAlertsPage }
page AdminUIAlertsPage {
authRequired: true,
component: import AdminUI from "@src/admin/elements/ui-elements/AlertsPage"
}
route AdminUIButtonsRoute { path: "/admin/ui/buttons", to: AdminUIButtonsPage }
page AdminUIButtonsPage {
authRequired: true,
component: import AdminUI from "@src/admin/elements/ui-elements/ButtonsPage"
}
//#endregion
//#region Contact Form Messages
// TODO:
// add functionality to allow users to send messages to admin
// and make them accessible via the admin dashboard
route AdminMessagesRoute { path: "/admin/messages", to: AdminMessagesPage }
page AdminMessagesPage {
authRequired: true,
component: import AdminMessages from "@src/messages/MessagesPage"
}
entity ContactFormMessage {=psl
id String @id @default(uuid())
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
userId String
content String
isRead Boolean @default(false)
repliedAt DateTime?
psl=}
//#endregion

View File

@@ -1,12 +1,12 @@
import { type User } from 'wasp/entities';
import { useQuery, getDailyStats } from 'wasp/client/operations';
import TotalSignupsCard from '../components/TotalSignupsCard';
import TotalPageViewsCard from '../components/TotalPaidViewsCard';
import TotalPayingUsersCard from '../components/TotalPayingUsersCard';
import TotalRevenueCard from '../components/TotalRevenueCard';
import RevenueAndProfitChart from '../components/RevenueAndProfitChart';
import SourcesTable from '../components/SourcesTable';
import DefaultLayout from '../layout/DefaultLayout';
import TotalSignupsCard from './TotalSignupsCard';
import TotalPageViewsCard from './TotalPageViewsCard';
import TotalPayingUsersCard from './TotalPayingUsersCard';
import TotalRevenueCard from './TotalRevenueCard';
import RevenueAndProfitChart from './RevenueAndProfitChart';
import SourcesTable from './PageViewSourcesTable';
import DefaultLayout from '../../layout/DefaultLayout';
import { useHistory } from 'react-router-dom';
const Dashboard = ({ user }: { user: User }) => {

View File

@@ -1,6 +1,6 @@
import { type PageViewSource } from 'wasp/entities';
const SourcesTable = ({ sources }: { sources: PageViewSource[] | undefined }) => {
const PageViewSourcesTable = ({ sources }: { sources: PageViewSource[] | undefined }) => {
return (
<div className='rounded-sm border border-stroke bg-white px-5 pt-6 pb-2.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1'>
<h4 className='mb-6 text-xl font-semibold text-black dark:text-white'>Top Sources</h4>
@@ -44,4 +44,4 @@ const SourcesTable = ({ sources }: { sources: PageViewSource[] | undefined }) =>
);
};
export default SourcesTable;
export default PageViewSourcesTable;

View File

@@ -1,7 +1,7 @@
import { ApexOptions } from 'apexcharts';
import React, { useState, useMemo, useEffect } from 'react';
import ReactApexChart from 'react-apexcharts';
import { DailyStatsProps } from '../common/types';
import { type DailyStatsProps } from '../../../analytics/stats';
const options: ApexOptions = {
legend: {

View File

@@ -1,5 +1,5 @@
import { cn } from '../../cn';
import { UpArrow, DownArrow } from '../images/icon/icons-arrows';
import { cn } from '../../../client/cn';
import { UpArrow, DownArrow } from '../../../client/icons/icons-arrows';
type PageViewsStats = {
totalPageViews: number | undefined;

View File

@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { UpArrow, DownArrow } from '../images/icon/icons-arrows';
import type { DailyStatsProps } from '../common/types';
import { cn } from '../../cn';
import { cn } from '../../../client/cn';
import { UpArrow, DownArrow } from '../../../client/icons/icons-arrows';
import { type DailyStatsProps } from '../../../analytics/stats';
const TotalPayingUsersCard = ({ dailyStats, isLoading }: DailyStatsProps) => {
const isDeltaPositive = useMemo(() => {

View File

@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { UpArrow, DownArrow } from '../images/icon/icons-arrows';
import type { DailyStatsProps } from '../common/types';
import { UpArrow, DownArrow } from '../../../client/icons/icons-arrows';
import { type DailyStatsProps } from '../../../analytics/stats';
const TotalRevenueCard = ({dailyStats, weeklyStats, isLoading}: DailyStatsProps) => {
const isDeltaPositive = useMemo(() => {

View File

@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { UpArrow } from '../images/icon/icons-arrows';
import type { DailyStatsProps } from '../common/types';
import { cn } from '../../cn';
import { cn } from '../../../client/cn';
import { UpArrow } from '../../../client/icons/icons-arrows';
import { type DailyStatsProps } from '../../../analytics/stats';
const TotalSignupsCard = ({ dailyStats, isLoading }: DailyStatsProps) => {
const isDeltaPositive = useMemo(() => {

View File

@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { cn } from '../../cn';
import { cn } from '../../../client/cn';
const DropdownDefault = () => {
const [dropdownOpen, setDropdownOpen] = useState(false);

View File

@@ -1,6 +1,6 @@
import { type User } from 'wasp/entities';
import { useState } from 'react';
import { cn } from '../../cn';
import { cn } from '../../../client/cn';
const SwitcherOne = ({ user, updateUserById }: { user?: Partial<User>; updateUserById?: any }) => {
const [enabled, setEnabled] = useState<boolean>(user?.isAdmin || false);

View File

@@ -0,0 +1,16 @@
import UsersTable from './UsersTable';
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
const Users = () => {
return (
<DefaultLayout>
<Breadcrumb pageName='Users' />
<div className='flex flex-col gap-10'>
<UsersTable />
</div>
</DefaultLayout>
);
};
export default Users;

View File

@@ -1,9 +1,9 @@
import { type SubscriptionStatus } from '../../../payment/plans';
import { updateUserById, useQuery, getPaginatedUsers } from 'wasp/client/operations';
import { useState, useEffect } from 'react';
import SwitcherOne from './SwitcherOne';
import Loader from '../common/Loader';
import LoadingSpinner from '../../layout/LoadingSpinner';
import DropdownEditDelete from './DropdownEditDelete';
import { type SubscriptionStatus } from '../../../payment/plans';
const UsersTable = () => {
const [skip, setskip] = useState(0);
@@ -188,7 +188,7 @@ const UsersTable = () => {
</div>
{isLoading && (
<div className='-mt-40'>
<Loader />
<LoadingSpinner />
</div>
)}
{!!data?.users &&

View File

@@ -1,5 +1,5 @@
import Breadcrumb from '../components/Breadcrumb';
import DefaultLayout from '../layout/DefaultLayout';
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
const Calendar = () => {
return (

View File

@@ -0,0 +1,23 @@
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
import BarChart from './BarChart';
import PieChart from './PieChart';
import DataStats from './DataStatsChart';
const Chart = () => {
return (
<DefaultLayout>
<Breadcrumb pageName='Chart' />
<div className='grid grid-cols-12 gap-4 md:gap-6 2xl:gap-7.5'>
<DataStats />
<div className='col-span-12'>
<BarChart />
</div>
<PieChart />
</div>
</DefaultLayout>
);
};
export default Chart;

View File

@@ -1,11 +1,11 @@
const DataStats = () => {
const DataStatsChart = () => {
return (
<div className="col-span-12 rounded-sm border border-stroke bg-white p-7.5 shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 xl:grid-cols-4 xl:gap-0">
<div className="flex items-center justify-center gap-2 border-b border-stroke pb-5 dark:border-strokedark xl:border-b-0 xl:border-r xl:pb-0">
<div>
<h4 className="mb-0.5 text-xl font-semibold text-black dark:text-white md:text-title-lg">
$4,350
4,350
</h4>
<p className="text-sm font-medium">Unique Visitors</p>
</div>
@@ -99,4 +99,4 @@ const DataStats = () => {
);
};
export default DataStats;
export default DataStatsChart;

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { cn } from '../../cn';
import { cn } from '../../../client/cn';
const CheckboxOne = () => {
const [isChecked, setIsChecked] = useState<boolean>(false);

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { cn } from '../../cn';
import { cn } from '../../../client/cn';
const CheckboxTwo = () => {
const [enabled, setEnabled] = useState<boolean>(false);

View File

@@ -0,0 +1,271 @@
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
import CheckboxOne from './CheckboxOne';
import SwitcherOne from '../../dashboards/users/SwitcherOne';
import SwitcherTwo from './SwitcherTwo';
const FormElements = () => {
return (
<DefaultLayout>
<Breadcrumb pageName='FormElements' />
<div className='grid grid-cols-1 gap-9 sm:grid-cols-2'>
<div className='flex flex-col gap-9'>
{/* <!-- Input Fields --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Input Fields</h3>
</div>
<div className='flex flex-col gap-5.5 p-6.5'>
<div>
<label className='mb-3 block text-black dark:text-white'>Default Input</label>
<input
type='text'
placeholder='Default Input'
className='w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div>
<label className='mb-3 block text-black dark:text-white'>Active Input</label>
<input
type='text'
placeholder='Active Input'
className='w-full rounded-lg border-[1.5px] border-primary bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:bg-form-input'
/>
</div>
<div>
<label className='mb-3 block font-medium text-black dark:text-white'>Disabled label</label>
<input
type='text'
placeholder='Disabled label'
disabled
className='w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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 dark:disabled:bg-black'
/>
</div>
</div>
</div>
{/* <!-- Toggle switch input --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Toggle switch input</h3>
</div>
<div className='flex flex-col gap-5.5 p-6.5'>
<SwitcherOne />
<SwitcherTwo />
</div>
</div>
{/* <!-- Time and date --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Time and date</h3>
</div>
<div className='flex flex-col gap-5.5 p-6.5'>
<div>
<label className='mb-3 block text-black dark:text-white'>Date picker</label>
<div className='relative'>
<input
type='date'
className='custom-input-date custom-input-date-1 w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary'
/>
</div>
</div>
<div>
<label className='mb-3 block text-black dark:text-white'>Select date</label>
<div className='relative'>
<input
type='date'
className='custom-input-date custom-input-date-2 w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary'
/>
</div>
</div>
</div>
</div>
{/* <!-- File upload --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>File upload</h3>
</div>
<div className='flex flex-col gap-5.5 p-6.5'>
<div>
<label className='mb-3 block text-black dark:text-white'>Attach file</label>
<input
type='file'
className='w-full cursor-pointer rounded-lg border-[1.5px] border-stroke bg-transparent font-medium outline-none transition file:mr-5 file:border-collapse file:cursor-pointer file:border-0 file:border-r file:border-solid file:border-stroke file:bg-whiter file:py-3 file:px-5 file:hover:bg-primary file:hover:bg-opacity-10 focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:file:border-form-strokedark dark:file:bg-white/30 dark:file:text-white dark:focus:border-primary'
/>
</div>
<div>
<label className='mb-3 block text-black dark:text-white'>Attach file</label>
<input
type='file'
className='w-full rounded-md border border-stroke p-3 outline-none transition file:mr-4 file:rounded file:border-[0.5px] file:border-stroke file:bg-[#EEEEEE] file:py-1 file:px-2.5 file:text-sm file:font-medium focus:border-primary file:focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:file:border-strokedark dark:file:bg-white/30 dark:file:text-white'
/>
</div>
</div>
</div>
</div>
<div className='flex flex-col gap-9'>
{/* <!-- Textarea Fields --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Textarea Fields</h3>
</div>
<div className='flex flex-col gap-5.5 p-6.5'>
<div>
<label className='mb-3 block text-black dark:text-white'>Default textarea</label>
<textarea
rows={6}
placeholder='Default textarea'
className='w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
></textarea>
</div>
<div>
<label className='mb-3 block text-black dark:text-white'>Active textarea</label>
<textarea
rows={6}
placeholder='Active textarea'
className='w-full rounded-lg border-[1.5px] border-primary bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:bg-form-input'
></textarea>
</div>
<div>
<label className='mb-3 block text-black dark:text-white'>Disabled textarea</label>
<textarea
rows={6}
disabled
placeholder='Disabled textarea'
className='w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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 dark:disabled:bg-black'
></textarea>
</div>
</div>
</div>
{/* <!-- Checkbox and radio --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Checkbox and radio</h3>
</div>
<div className='flex flex-col gap-5.5 p-6.5'>
<CheckboxOne />
</div>
</div>
{/* <!-- Select input --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Select input</h3>
</div>
<div className='flex flex-col gap-5.5 p-6.5'>
<div>
<label className='mb-3 block text-black dark:text-white'>Select Country</label>
<div className='relative z-20 bg-white dark:bg-form-input'>
<span className='absolute top-1/2 left-4 z-30 -translate-y-1/2'>
<svg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'>
<g opacity='0.8'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M10.0007 2.50065C5.85852 2.50065 2.50065 5.85852 2.50065 10.0007C2.50065 14.1428 5.85852 17.5007 10.0007 17.5007C14.1428 17.5007 17.5007 14.1428 17.5007 10.0007C17.5007 5.85852 14.1428 2.50065 10.0007 2.50065ZM0.833984 10.0007C0.833984 4.93804 4.93804 0.833984 10.0007 0.833984C15.0633 0.833984 19.1673 4.93804 19.1673 10.0007C19.1673 15.0633 15.0633 19.1673 10.0007 19.1673C4.93804 19.1673 0.833984 15.0633 0.833984 10.0007Z'
fill='#637381'
></path>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M0.833984 9.99935C0.833984 9.53911 1.20708 9.16602 1.66732 9.16602H18.334C18.7942 9.16602 19.1673 9.53911 19.1673 9.99935C19.1673 10.4596 18.7942 10.8327 18.334 10.8327H1.66732C1.20708 10.8327 0.833984 10.4596 0.833984 9.99935Z'
fill='#637381'
></path>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M7.50084 10.0008C7.55796 12.5632 8.4392 15.0301 10.0006 17.0418C11.5621 15.0301 12.4433 12.5632 12.5005 10.0008C12.4433 7.43845 11.5621 4.97153 10.0007 2.95982C8.4392 4.97153 7.55796 7.43845 7.50084 10.0008ZM10.0007 1.66749L9.38536 1.10547C7.16473 3.53658 5.90275 6.69153 5.83417 9.98346C5.83392 9.99503 5.83392 10.0066 5.83417 10.0182C5.90275 13.3101 7.16473 16.4651 9.38536 18.8962C9.54325 19.069 9.76655 19.1675 10.0007 19.1675C10.2348 19.1675 10.4581 19.069 10.6159 18.8962C12.8366 16.4651 14.0986 13.3101 14.1671 10.0182C14.1674 10.0066 14.1674 9.99503 14.1671 9.98346C14.0986 6.69153 12.8366 3.53658 10.6159 1.10547L10.0007 1.66749Z'
fill='#637381'
></path>
</g>
</svg>
</span>
<select className='relative z-20 w-full appearance-none rounded border border-stroke bg-transparent py-3 px-12 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input'>
<option value=''>USA</option>
<option value=''>UK</option>
<option value=''>Canada</option>
</select>
<span className='absolute top-1/2 right-4 z-10 -translate-y-1/2'>
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<g opacity='0.8'>
<path
fillRule='evenodd'
clipRule='evenodd'
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'
fill='#637381'
></path>
</g>
</svg>
</span>
</div>
</div>
<div>
<label className='mb-3 block text-black dark:text-white'>Multiselect Dropdown</label>
<div className='relative z-20 w-full rounded border border-stroke p-1.5 pr-8 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input'>
<div className='flex flex-wrap items-center'>
<span className='m-1.5 flex items-center justify-center rounded border-[.5px] border-stroke bg-gray py-1.5 px-2.5 text-sm font-medium dark:border-strokedark dark:bg-white/30'>
Design
<span className='cursor-pointer pl-2 hover:text-danger'>
<svg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
fillRule='evenodd'
clipRule='evenodd'
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'
fill='currentColor'
></path>
</svg>
</span>
</span>
<span className='m-1.5 flex items-center justify-center rounded border-[.5px] border-stroke bg-gray py-1.5 px-2.5 text-sm font-medium dark:border-strokedark dark:bg-white/30'>
Development
<span className='cursor-pointer pl-2 hover:text-danger'>
<svg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
fillRule='evenodd'
clipRule='evenodd'
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'
fill='currentColor'
></path>
</svg>
</span>
</span>
</div>
<select name='' id='' className='absolute top-0 left-0 z-20 h-full w-full bg-transparent opacity-0'>
<option value=''>Option</option>
<option value=''>Option</option>
</select>
<span className='absolute top-1/2 right-4 z-10 -translate-y-1/2'>
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<g opacity='0.8'>
<path
fillRule='evenodd'
clipRule='evenodd'
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'
fill='#637381'
></path>
</g>
</svg>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</DefaultLayout>
);
};
export default FormElements;

View File

@@ -0,0 +1,226 @@
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
const FormLayout = () => {
return (
<DefaultLayout>
<Breadcrumb pageName='FormLayout' />
<div className='grid grid-cols-1 gap-9 sm:grid-cols-2'>
<div className='flex flex-col gap-9'>
{/* <!-- Contact Form --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Contact Form</h3>
</div>
<form action='#'>
<div className='p-6.5'>
<div className='mb-4.5 flex flex-col gap-6 xl:flex-row'>
<div className='w-full xl:w-1/2'>
<label className='mb-2.5 block text-black dark:text-white'>First name</label>
<input
type='text'
placeholder='Enter your first name'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div className='w-full xl:w-1/2'>
<label className='mb-2.5 block text-black dark:text-white'>Last name</label>
<input
type='text'
placeholder='Enter your last name'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
</div>
<div className='mb-4.5'>
<label className='mb-2.5 block text-black dark:text-white'>
Email <span className='text-meta-1'>*</span>
</label>
<input
type='email'
placeholder='Enter your email address'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div className='mb-4.5'>
<label className='mb-2.5 block text-black dark:text-white'>Subject</label>
<input
type='text'
placeholder='Select subject'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div className='mb-4.5'>
<label className='mb-2.5 block text-black dark:text-white'>Subject</label>
<div className='relative z-20 bg-transparent dark:bg-form-input'>
<select className='relative z-20 w-full appearance-none rounded border border-stroke bg-transparent py-3 px-5 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary'>
<option value=''>Type your subject</option>
<option value=''>USA</option>
<option value=''>UK</option>
<option value=''>Canada</option>
</select>
<span className='absolute top-1/2 right-4 z-30 -translate-y-1/2'>
<svg
className='fill-current'
width='24'
height='24'
viewBox='0 0 24 24'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<g opacity='0.8'>
<path
fillRule='evenodd'
clipRule='evenodd'
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'
fill=''
></path>
</g>
</svg>
</span>
</div>
</div>
<div className='mb-6'>
<label className='mb-2.5 block text-black dark:text-white'>Message</label>
<textarea
rows={6}
placeholder='Type your message'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
></textarea>
</div>
<button className='flex w-full justify-center rounded bg-primary p-3 font-medium text-gray'>
Send Message
</button>
</div>
</form>
</div>
</div>
<div className='flex flex-col gap-9'>
{/* <!-- Sign In Form --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Sign In Form</h3>
</div>
<form action='#'>
<div className='p-6.5'>
<div className='mb-4.5'>
<label className='mb-2.5 block text-black dark:text-white'>Email</label>
<input
type='email'
placeholder='Enter your email address'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div>
<label className='mb-2.5 block text-black dark:text-white'>Password</label>
<input
type='password'
placeholder='Enter password'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div className='mt-5 mb-5.5 flex items-center justify-between'>
<label htmlFor='formCheckbox' className='flex cursor-pointer'>
<div className='relative pt-0.5'>
<input type='checkbox' id='formCheckbox' className='taskCheckbox sr-only' />
<div className='box mr-3 flex h-5 w-5 items-center justify-center rounded border border-stroke dark:border-strokedark'>
<span className='text-white opacity-0'>
<svg
className='fill-current'
width='10'
height='7'
viewBox='0 0 10 7'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M9.70685 0.292804C9.89455 0.480344 10 0.734667 10 0.999847C10 1.26503 9.89455 1.51935 9.70685 1.70689L4.70059 6.7072C4.51283 6.89468 4.2582 7 3.9927 7C3.72721 7 3.47258 6.89468 3.28482 6.7072L0.281063 3.70701C0.0986771 3.5184 -0.00224342 3.26578 3.785e-05 3.00357C0.00231912 2.74136 0.10762 2.49053 0.29326 2.30511C0.4789 2.11969 0.730026 2.01451 0.992551 2.01224C1.25508 2.00996 1.50799 2.11076 1.69683 2.29293L3.9927 4.58607L8.29108 0.292804C8.47884 0.105322 8.73347 0 8.99896 0C9.26446 0 9.51908 0.105322 9.70685 0.292804Z'
fill=''
/>
</svg>
</span>
</div>
</div>
<p>Remember me</p>
</label>
<a href='#' className='text-sm text-primary'>
Forget password?
</a>
</div>
<button className='flex w-full justify-center rounded bg-primary p-3 font-medium text-gray'>
Sign In
</button>
</div>
</form>
</div>
{/* <!-- Sign Up Form --> */}
<div className='rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark'>
<div className='border-b border-stroke py-4 px-6.5 dark:border-strokedark'>
<h3 className='font-medium text-black dark:text-white'>Sign Up Form</h3>
</div>
<form action='#'>
<div className='p-6.5'>
<div className='mb-4.5'>
<label className='mb-2.5 block text-black dark:text-white'>Name</label>
<input
type='text'
placeholder='Enter your full name'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div className='mb-4.5'>
<label className='mb-2.5 block text-black dark:text-white'>Email</label>
<input
type='email'
placeholder='Enter your email address'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div className='mb-4.5'>
<label className='mb-2.5 block text-black dark:text-white'>Password</label>
<input
type='password'
placeholder='Enter password'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<div className='mb-5.5'>
<label className='mb-2.5 block text-black dark:text-white'>Re-type Password</label>
<input
type='password'
placeholder='Re-enter password'
className='w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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'
/>
</div>
<button className='flex w-full justify-center rounded bg-primary p-3 font-medium text-gray'>
Sign Up
</button>
</div>
</form>
</div>
</div>
</div>
</DefaultLayout>
);
};
export default FormLayout;

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { cn } from '../../cn';
import { cn } from '../../../client/cn';
const SwitcherTwo = () => {
const [enabled, setEnabled] = useState(false);

View File

@@ -1,9 +1,9 @@
import { FormEvent } from 'react';
import Breadcrumb from '../components/Breadcrumb';
import toast from 'react-hot-toast';
import DefaultLayout from '../layout/DefaultLayout';
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
const Settings = () => {
const SettingsPage = () => {
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
// TODO add toast provider / wrapper
event.preventDefault();
@@ -322,4 +322,4 @@ const Settings = () => {
);
};
export default Settings;
export default SettingsPage;

View File

@@ -1,92 +1,64 @@
import Breadcrumb from '../../components/Breadcrumb';
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
const Alerts = () => {
return (
<DefaultLayout>
<Breadcrumb pageName="Alerts" />
<Breadcrumb pageName='Alerts' />
<div className="rounded-sm border border-stroke bg-white p-4 shadow-default dark:border-strokedark dark:bg-boxdark md:p-6 xl:p-9">
<div className="flex flex-col gap-7.5">
<div className='rounded-sm border border-stroke bg-white p-4 shadow-default dark:border-strokedark dark:bg-boxdark md:p-6 xl:p-9'>
<div className='flex flex-col gap-7.5'>
{/* <!-- Alerts Item --> */}
<div className="flex w-full border-l-6 border-warning bg-warning bg-opacity-[15%] px-7 py-8 shadow-md dark:bg-[#1B1B24] dark:bg-opacity-30 md:p-9">
<div className="mr-5 flex h-9 w-9 items-center justify-center rounded-lg bg-warning bg-opacity-30">
<svg
width="19"
height="16"
viewBox="0 0 19 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<div className='flex w-full border-l-6 border-warning bg-warning bg-opacity-[15%] px-7 py-8 shadow-md dark:bg-[#1B1B24] dark:bg-opacity-30 md:p-9'>
<div className='mr-5 flex h-9 w-9 items-center justify-center rounded-lg bg-warning bg-opacity-30'>
<svg width='19' height='16' viewBox='0 0 19 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d="M1.50493 16H17.5023C18.6204 16 19.3413 14.9018 18.8354 13.9735L10.8367 0.770573C10.2852 -0.256858 8.70677 -0.256858 8.15528 0.770573L0.156617 13.9735C-0.334072 14.8998 0.386764 16 1.50493 16ZM10.7585 12.9298C10.7585 13.6155 10.2223 14.1433 9.45583 14.1433C8.6894 14.1433 8.15311 13.6155 8.15311 12.9298V12.9015C8.15311 12.2159 8.6894 11.688 9.45583 11.688C10.2223 11.688 10.7585 12.2159 10.7585 12.9015V12.9298ZM8.75236 4.01062H10.2548C10.6674 4.01062 10.9127 4.33826 10.8671 4.75288L10.2071 10.1186C10.1615 10.5049 9.88572 10.7455 9.50142 10.7455C9.11929 10.7455 8.84138 10.5028 8.79579 10.1186L8.13574 4.75288C8.09449 4.33826 8.33984 4.01062 8.75236 4.01062Z"
fill="#FBBF24"
d='M1.50493 16H17.5023C18.6204 16 19.3413 14.9018 18.8354 13.9735L10.8367 0.770573C10.2852 -0.256858 8.70677 -0.256858 8.15528 0.770573L0.156617 13.9735C-0.334072 14.8998 0.386764 16 1.50493 16ZM10.7585 12.9298C10.7585 13.6155 10.2223 14.1433 9.45583 14.1433C8.6894 14.1433 8.15311 13.6155 8.15311 12.9298V12.9015C8.15311 12.2159 8.6894 11.688 9.45583 11.688C10.2223 11.688 10.7585 12.2159 10.7585 12.9015V12.9298ZM8.75236 4.01062H10.2548C10.6674 4.01062 10.9127 4.33826 10.8671 4.75288L10.2071 10.1186C10.1615 10.5049 9.88572 10.7455 9.50142 10.7455C9.11929 10.7455 8.84138 10.5028 8.79579 10.1186L8.13574 4.75288C8.09449 4.33826 8.33984 4.01062 8.75236 4.01062Z'
fill='#FBBF24'
></path>
</svg>
</div>
<div className="w-full">
<h5 className="mb-3 text-lg font-semibold text-[#9D5425]">
Attention needed
</h5>
<p className="leading-relaxed text-[#D0915C]">
Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industry's standard dummy
text ever since the 1500s, when
<div className='w-full'>
<h5 className='mb-3 text-lg font-semibold text-[#9D5425]'>Attention needed</h5>
<p className='leading-relaxed text-[#D0915C]'>
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the
industry's standard dummy text ever since the 1500s, when
</p>
</div>
</div>
{/* <!-- Alerts Item --> */}
<div className="flex w-full border-l-6 border-[#34D399] bg-[#34D399] bg-opacity-[15%] px-7 py-8 shadow-md dark:bg-[#1B1B24] dark:bg-opacity-30 md:p-9">
<div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#34D399]">
<svg
width="16"
height="12"
viewBox="0 0 16 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<div className='flex w-full border-l-6 border-[#34D399] bg-[#34D399] bg-opacity-[15%] px-7 py-8 shadow-md dark:bg-[#1B1B24] dark:bg-opacity-30 md:p-9'>
<div className='mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#34D399]'>
<svg width='16' height='12' viewBox='0 0 16 12' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d="M15.2984 0.826822L15.2868 0.811827L15.2741 0.797751C14.9173 0.401867 14.3238 0.400754 13.9657 0.794406L5.91888 9.45376L2.05667 5.2868C1.69856 4.89287 1.10487 4.89389 0.747996 5.28987C0.417335 5.65675 0.417335 6.22337 0.747996 6.59026L0.747959 6.59029L0.752701 6.59541L4.86742 11.0348C5.14445 11.3405 5.52858 11.5 5.89581 11.5C6.29242 11.5 6.65178 11.3355 6.92401 11.035L15.2162 2.11161C15.5833 1.74452 15.576 1.18615 15.2984 0.826822Z"
fill="white"
stroke="white"
d='M15.2984 0.826822L15.2868 0.811827L15.2741 0.797751C14.9173 0.401867 14.3238 0.400754 13.9657 0.794406L5.91888 9.45376L2.05667 5.2868C1.69856 4.89287 1.10487 4.89389 0.747996 5.28987C0.417335 5.65675 0.417335 6.22337 0.747996 6.59026L0.747959 6.59029L0.752701 6.59541L4.86742 11.0348C5.14445 11.3405 5.52858 11.5 5.89581 11.5C6.29242 11.5 6.65178 11.3355 6.92401 11.035L15.2162 2.11161C15.5833 1.74452 15.576 1.18615 15.2984 0.826822Z'
fill='white'
stroke='white'
></path>
</svg>
</div>
<div className="w-full">
<h5 className="mb-3 text-lg font-semibold text-black dark:text-[#34D399] ">
Message Sent Successfully
</h5>
<p className="text-base leading-relaxed text-body">
Lorem Ipsum is simply dummy text of the printing and typesetting
industry.
<div className='w-full'>
<h5 className='mb-3 text-lg font-semibold text-black dark:text-[#34D399] '>Message Sent Successfully</h5>
<p className='text-base leading-relaxed text-body'>
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
</p>
</div>
</div>
{/* <!-- Alerts Item --> */}
<div className="flex w-full border-l-6 border-[#F87171] bg-[#F87171] bg-opacity-[15%] px-7 py-8 shadow-md dark:bg-[#1B1B24] dark:bg-opacity-30 md:p-9">
<div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#F87171]">
<svg
width="13"
height="13"
viewBox="0 0 13 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<div className='flex w-full border-l-6 border-[#F87171] bg-[#F87171] bg-opacity-[15%] px-7 py-8 shadow-md dark:bg-[#1B1B24] dark:bg-opacity-30 md:p-9'>
<div className='mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#F87171]'>
<svg width='13' height='13' viewBox='0 0 13 13' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d="M6.4917 7.65579L11.106 12.2645C11.2545 12.4128 11.4715 12.5 11.6738 12.5C11.8762 12.5 12.0931 12.4128 12.2416 12.2645C12.5621 11.9445 12.5623 11.4317 12.2423 11.1114C12.2422 11.1113 12.2422 11.1113 12.2422 11.1113C12.242 11.1111 12.2418 11.1109 12.2416 11.1107L7.64539 6.50351L12.2589 1.91221L12.2595 1.91158C12.5802 1.59132 12.5802 1.07805 12.2595 0.757793C11.9393 0.437994 11.4268 0.437869 11.1064 0.757418C11.1063 0.757543 11.1062 0.757668 11.106 0.757793L6.49234 5.34931L1.89459 0.740581L1.89396 0.739942C1.57364 0.420019 1.0608 0.420019 0.740487 0.739944C0.42005 1.05999 0.419837 1.57279 0.73985 1.89309L6.4917 7.65579ZM6.4917 7.65579L1.89459 12.2639L1.89395 12.2645C1.74546 12.4128 1.52854 12.5 1.32616 12.5C1.12377 12.5 0.906853 12.4128 0.758361 12.2645L1.1117 11.9108L0.758358 12.2645C0.437984 11.9445 0.437708 11.4319 0.757539 11.1116C0.757812 11.1113 0.758086 11.111 0.75836 11.1107L5.33864 6.50287L0.740487 1.89373L6.4917 7.65579Z"
fill="#ffffff"
stroke="#ffffff"
d='M6.4917 7.65579L11.106 12.2645C11.2545 12.4128 11.4715 12.5 11.6738 12.5C11.8762 12.5 12.0931 12.4128 12.2416 12.2645C12.5621 11.9445 12.5623 11.4317 12.2423 11.1114C12.2422 11.1113 12.2422 11.1113 12.2422 11.1113C12.242 11.1111 12.2418 11.1109 12.2416 11.1107L7.64539 6.50351L12.2589 1.91221L12.2595 1.91158C12.5802 1.59132 12.5802 1.07805 12.2595 0.757793C11.9393 0.437994 11.4268 0.437869 11.1064 0.757418C11.1063 0.757543 11.1062 0.757668 11.106 0.757793L6.49234 5.34931L1.89459 0.740581L1.89396 0.739942C1.57364 0.420019 1.0608 0.420019 0.740487 0.739944C0.42005 1.05999 0.419837 1.57279 0.73985 1.89309L6.4917 7.65579ZM6.4917 7.65579L1.89459 12.2639L1.89395 12.2645C1.74546 12.4128 1.52854 12.5 1.32616 12.5C1.12377 12.5 0.906853 12.4128 0.758361 12.2645L1.1117 11.9108L0.758358 12.2645C0.437984 11.9445 0.437708 11.4319 0.757539 11.1116C0.757812 11.1113 0.758086 11.111 0.75836 11.1107L5.33864 6.50287L0.740487 1.89373L6.4917 7.65579Z'
fill='#ffffff'
stroke='#ffffff'
></path>
</svg>
</div>
<div className="w-full">
<h5 className="mb-3 font-semibold text-[#B45454]">
There were 1 errors with your submission
</h5>
<div className='w-full'>
<h5 className='mb-3 font-semibold text-[#B45454]'>There were 1 errors with your submission</h5>
<ul>
<li className="leading-relaxed text-[#CD5D5D]">
Lorem Ipsum is simply dummy text of the printing
</li>
<li className='leading-relaxed text-[#CD5D5D]'>Lorem Ipsum is simply dummy text of the printing</li>
</ul>
</div>
</div>

View File

@@ -1,5 +1,5 @@
import { Link } from 'react-router-dom';
import Breadcrumb from '../../components/Breadcrumb';
import Breadcrumb from '../../layout/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
const Buttons = () => {

View File

@@ -1,7 +1,7 @@
import { useAuth } from 'wasp/client/auth';
import { useState, ReactNode, FC } from 'react';
import Header from '../components/Header';
import Sidebar from '../components/Sidebar';
import Header from './Header';
import Sidebar from './Sidebar';
interface Props {
children?: ReactNode;

View File

@@ -1,8 +1,8 @@
import { type AuthUser } from 'wasp/auth/types';
import DarkModeSwitcher from './DarkModeSwitcher';
import MessageButton from './MessageButton';
import DropdownUser from '../../../user/DropdownUser';
import { cn } from '../../cn';
import MessageButton from '../../messages/MessageButton';
import DropdownUser from '../../user/DropdownUser';
import { cn } from '../../client/cn';
import DarkModeSwitcher from '../../client/components/DarkModeSwitcher';
const Header = (props: {
sidebarOpen: string | boolean | undefined;

View File

@@ -1,4 +1,4 @@
const Loader = () => {
const LoadingSpinner = () => {
return (
<div className="flex h-screen items-center justify-center bg-white">
<div className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent"></div>
@@ -6,4 +6,4 @@ const Loader = () => {
);
};
export default Loader;
export default LoadingSpinner;

View File

@@ -1,8 +1,8 @@
import React, { useEffect, useRef, useState } from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import Logo from '../../static/logo.png';
import Logo from '../../client/static/logo.png';
import SidebarLinkGroup from './SidebarLinkGroup';
import { cn } from '../../cn';
import { cn } from '../../client/cn';
interface SidebarProps {
sidebarOpen: boolean;

View File

@@ -0,0 +1,38 @@
import { type DailyStats, type PageViewSource } from 'wasp/entities';
import { HttpError } from 'wasp/server';
import { type GetDailyStats } from 'wasp/server/operations';
type DailyStatsWithSources = DailyStats & {
sources: PageViewSource[];
};
type DailyStatsValues = {
dailyStats: DailyStatsWithSources;
weeklyStats: DailyStatsWithSources[];
};
export const getDailyStats: GetDailyStats<void, DailyStatsValues> = async (_args, context) => {
if (!context.user?.isAdmin) {
throw new HttpError(401);
}
const dailyStats = await context.entities.DailyStats.findFirstOrThrow({
orderBy: {
date: 'desc',
},
include: {
sources: true,
},
});
const weeklyStats = await context.entities.DailyStats.findMany({
orderBy: {
date: 'desc',
},
take: 7,
include: {
sources: true,
},
});
return { dailyStats, weeklyStats };
};

View File

@@ -39,7 +39,10 @@ async function getTotalPageViews() {
`${PLAUSIBLE_BASE_URL}/v1/stats/aggregate?site_id=${PLAUSIBLE_SITE_ID}&metrics=pageviews`,
{
method: 'GET',
headers: headers,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${PLAUSIBLE_API_KEY}`,
},
}
);
if (!response.ok) {

View File

@@ -1,8 +1,11 @@
import { type DailyStats } from 'wasp/entities';
import { type DailyStatsJob } from 'wasp/server/jobs';
import Stripe from 'stripe';
import { stripe } from '../../payment/stripe/stripeClient';
import { getDailyPageViews, getSources } from './plausibleAnalyticsUtils.js';
// import { getDailyPageViews, getSources } from './googleAnalyticsUtils.js';
import { stripe } from '../payment/stripe/stripeClient'
import { getDailyPageViews, getSources } from './providers/plausibleAnalyticsUtils';
// import { getDailyPageViews, getSources } from './providers/googleAnalyticsUtils;
export type DailyStatsProps = { dailyStats?: DailyStats; weeklyStats?: DailyStats[]; isLoading?: boolean };
export const calculateDailyStats: DailyStatsJob<never, void> = async (_args, context) => {
const nowUTC = new Date(Date.now());

View File

@@ -1,3 +0,0 @@
import { type DailyStats } from 'wasp/entities';
export type DailyStatsProps = { dailyStats?: DailyStats; weeklyStats?: DailyStats[]; isLoading?: boolean };

View File

@@ -1,23 +0,0 @@
import Breadcrumb from '../components/Breadcrumb';
import BarChart from '../components/BarChart';
import PieChart from '../components/PieChart';
import DataStats from '../components/DataStats';
import DefaultLayout from '../layout/DefaultLayout';
const Chart = () => {
return (
<DefaultLayout>
<Breadcrumb pageName="Chart" />
<div className="grid grid-cols-12 gap-4 md:gap-6 2xl:gap-7.5">
<DataStats />
<div className="col-span-12">
<BarChart />
</div>
<PieChart />
</div>
</DefaultLayout>
);
};
export default Chart;

View File

@@ -1,343 +0,0 @@
import Breadcrumb from '../../components/Breadcrumb';
import CheckboxOne from '../../components/CheckboxOne';
import SwitcherOne from '../../components/SwitcherOne';
import SwitcherTwo from '../../components/SwitcherTwo';
import DefaultLayout from '../../layout/DefaultLayout';
const FormElements = () => {
return (
<DefaultLayout>
<Breadcrumb pageName="FormElements" />
<div className="grid grid-cols-1 gap-9 sm:grid-cols-2">
<div className="flex flex-col gap-9">
{/* <!-- Input Fields --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Input Fields
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-black dark:text-white">
Default Input
</label>
<input
type="text"
placeholder="Default Input"
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div>
<label className="mb-3 block text-black dark:text-white">
Active Input
</label>
<input
type="text"
placeholder="Active Input"
className="w-full rounded-lg border-[1.5px] border-primary bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:bg-form-input"
/>
</div>
<div>
<label className="mb-3 block font-medium text-black dark:text-white">
Disabled label
</label>
<input
type="text"
placeholder="Disabled label"
disabled
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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 dark:disabled:bg-black"
/>
</div>
</div>
</div>
{/* <!-- Toggle switch input --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Toggle switch input
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<SwitcherOne />
<SwitcherTwo />
</div>
</div>
{/* <!-- Time and date --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Time and date
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-black dark:text-white">
Date picker
</label>
<div className="relative">
<input
type="date"
className="custom-input-date custom-input-date-1 w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary"
/>
</div>
</div>
<div>
<label className="mb-3 block text-black dark:text-white">
Select date
</label>
<div className="relative">
<input
type="date"
className="custom-input-date custom-input-date-2 w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary"
/>
</div>
</div>
</div>
</div>
{/* <!-- File upload --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
File upload
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-black dark:text-white">
Attach file
</label>
<input
type="file"
className="w-full cursor-pointer rounded-lg border-[1.5px] border-stroke bg-transparent font-medium outline-none transition file:mr-5 file:border-collapse file:cursor-pointer file:border-0 file:border-r file:border-solid file:border-stroke file:bg-whiter file:py-3 file:px-5 file:hover:bg-primary file:hover:bg-opacity-10 focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:file:border-form-strokedark dark:file:bg-white/30 dark:file:text-white dark:focus:border-primary"
/>
</div>
<div>
<label className="mb-3 block text-black dark:text-white">
Attach file
</label>
<input
type="file"
className="w-full rounded-md border border-stroke p-3 outline-none transition file:mr-4 file:rounded file:border-[0.5px] file:border-stroke file:bg-[#EEEEEE] file:py-1 file:px-2.5 file:text-sm file:font-medium focus:border-primary file:focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:file:border-strokedark dark:file:bg-white/30 dark:file:text-white"
/>
</div>
</div>
</div>
</div>
<div className="flex flex-col gap-9">
{/* <!-- Textarea Fields --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Textarea Fields
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-black dark:text-white">
Default textarea
</label>
<textarea
rows={6}
placeholder="Default textarea"
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
></textarea>
</div>
<div>
<label className="mb-3 block text-black dark:text-white">
Active textarea
</label>
<textarea
rows={6}
placeholder="Active textarea"
className="w-full rounded-lg border-[1.5px] border-primary bg-transparent py-3 px-5 font-medium outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:bg-form-input"
></textarea>
</div>
<div>
<label className="mb-3 block text-black dark:text-white">
Disabled textarea
</label>
<textarea
rows={6}
disabled
placeholder="Disabled textarea"
className="w-full rounded-lg border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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 dark:disabled:bg-black"
></textarea>
</div>
</div>
</div>
{/* <!-- Checkbox and radio --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Checkbox and radio
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<CheckboxOne />
</div>
</div>
{/* <!-- Select input --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Select input
</h3>
</div>
<div className="flex flex-col gap-5.5 p-6.5">
<div>
<label className="mb-3 block text-black dark:text-white">
Select Country
</label>
<div className="relative z-20 bg-white dark:bg-form-input">
<span className="absolute top-1/2 left-4 z-30 -translate-y-1/2">
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.0007 2.50065C5.85852 2.50065 2.50065 5.85852 2.50065 10.0007C2.50065 14.1428 5.85852 17.5007 10.0007 17.5007C14.1428 17.5007 17.5007 14.1428 17.5007 10.0007C17.5007 5.85852 14.1428 2.50065 10.0007 2.50065ZM0.833984 10.0007C0.833984 4.93804 4.93804 0.833984 10.0007 0.833984C15.0633 0.833984 19.1673 4.93804 19.1673 10.0007C19.1673 15.0633 15.0633 19.1673 10.0007 19.1673C4.93804 19.1673 0.833984 15.0633 0.833984 10.0007Z"
fill="#637381"
></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0.833984 9.99935C0.833984 9.53911 1.20708 9.16602 1.66732 9.16602H18.334C18.7942 9.16602 19.1673 9.53911 19.1673 9.99935C19.1673 10.4596 18.7942 10.8327 18.334 10.8327H1.66732C1.20708 10.8327 0.833984 10.4596 0.833984 9.99935Z"
fill="#637381"
></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.50084 10.0008C7.55796 12.5632 8.4392 15.0301 10.0006 17.0418C11.5621 15.0301 12.4433 12.5632 12.5005 10.0008C12.4433 7.43845 11.5621 4.97153 10.0007 2.95982C8.4392 4.97153 7.55796 7.43845 7.50084 10.0008ZM10.0007 1.66749L9.38536 1.10547C7.16473 3.53658 5.90275 6.69153 5.83417 9.98346C5.83392 9.99503 5.83392 10.0066 5.83417 10.0182C5.90275 13.3101 7.16473 16.4651 9.38536 18.8962C9.54325 19.069 9.76655 19.1675 10.0007 19.1675C10.2348 19.1675 10.4581 19.069 10.6159 18.8962C12.8366 16.4651 14.0986 13.3101 14.1671 10.0182C14.1674 10.0066 14.1674 9.99503 14.1671 9.98346C14.0986 6.69153 12.8366 3.53658 10.6159 1.10547L10.0007 1.66749Z"
fill="#637381"
></path>
</g>
</svg>
</span>
<select className="relative z-20 w-full appearance-none rounded border border-stroke bg-transparent py-3 px-12 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input">
<option value="">USA</option>
<option value="">UK</option>
<option value="">Canada</option>
</select>
<span className="absolute top-1/2 right-4 z-10 -translate-y-1/2">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
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"
fill="#637381"
></path>
</g>
</svg>
</span>
</div>
</div>
<div>
<label className="mb-3 block text-black dark:text-white">
Multiselect Dropdown
</label>
<div className="relative z-20 w-full rounded border border-stroke p-1.5 pr-8 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input">
<div className="flex flex-wrap items-center">
<span className="m-1.5 flex items-center justify-center rounded border-[.5px] border-stroke bg-gray py-1.5 px-2.5 text-sm font-medium dark:border-strokedark dark:bg-white/30">
Design
<span className="cursor-pointer pl-2 hover:text-danger">
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
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"
fill="currentColor"
></path>
</svg>
</span>
</span>
<span className="m-1.5 flex items-center justify-center rounded border-[.5px] border-stroke bg-gray py-1.5 px-2.5 text-sm font-medium dark:border-strokedark dark:bg-white/30">
Development
<span className="cursor-pointer pl-2 hover:text-danger">
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
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"
fill="currentColor"
></path>
</svg>
</span>
</span>
</div>
<select
name=""
id=""
className="absolute top-0 left-0 z-20 h-full w-full bg-transparent opacity-0"
>
<option value="">Option</option>
<option value="">Option</option>
</select>
<span className="absolute top-1/2 right-4 z-10 -translate-y-1/2">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
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"
fill="#637381"
></path>
</g>
</svg>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</DefaultLayout>
);
};
export default FormElements;

View File

@@ -1,258 +0,0 @@
import Breadcrumb from '../../components/Breadcrumb';
import DefaultLayout from '../../layout/DefaultLayout';
const FormLayout = () => {
return (
<DefaultLayout>
<Breadcrumb pageName="FormLayout" />
<div className="grid grid-cols-1 gap-9 sm:grid-cols-2">
<div className="flex flex-col gap-9">
{/* <!-- Contact Form --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Contact Form
</h3>
</div>
<form action="#">
<div className="p-6.5">
<div className="mb-4.5 flex flex-col gap-6 xl:flex-row">
<div className="w-full xl:w-1/2">
<label className="mb-2.5 block text-black dark:text-white">
First name
</label>
<input
type="text"
placeholder="Enter your first name"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div className="w-full xl:w-1/2">
<label className="mb-2.5 block text-black dark:text-white">
Last name
</label>
<input
type="text"
placeholder="Enter your last name"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
</div>
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
Email <span className="text-meta-1">*</span>
</label>
<input
type="email"
placeholder="Enter your email address"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
Subject
</label>
<input
type="text"
placeholder="Select subject"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
Subject
</label>
<div className="relative z-20 bg-transparent dark:bg-form-input">
<select className="relative z-20 w-full appearance-none rounded border border-stroke bg-transparent py-3 px-5 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary">
<option value="">Type your subject</option>
<option value="">USA</option>
<option value="">UK</option>
<option value="">Canada</option>
</select>
<span className="absolute top-1/2 right-4 z-30 -translate-y-1/2">
<svg
className="fill-current"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g opacity="0.8">
<path
fillRule="evenodd"
clipRule="evenodd"
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"
fill=""
></path>
</g>
</svg>
</span>
</div>
</div>
<div className="mb-6">
<label className="mb-2.5 block text-black dark:text-white">
Message
</label>
<textarea
rows={6}
placeholder="Type your message"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
></textarea>
</div>
<button className="flex w-full justify-center rounded bg-primary p-3 font-medium text-gray">
Send Message
</button>
</div>
</form>
</div>
</div>
<div className="flex flex-col gap-9">
{/* <!-- Sign In Form --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Sign In Form
</h3>
</div>
<form action="#">
<div className="p-6.5">
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
Email
</label>
<input
type="email"
placeholder="Enter your email address"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div>
<label className="mb-2.5 block text-black dark:text-white">
Password
</label>
<input
type="password"
placeholder="Enter password"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div className="mt-5 mb-5.5 flex items-center justify-between">
<label htmlFor="formCheckbox" className="flex cursor-pointer">
<div className="relative pt-0.5">
<input
type="checkbox"
id="formCheckbox"
className="taskCheckbox sr-only"
/>
<div className="box mr-3 flex h-5 w-5 items-center justify-center rounded border border-stroke dark:border-strokedark">
<span className="text-white opacity-0">
<svg
className="fill-current"
width="10"
height="7"
viewBox="0 0 10 7"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.70685 0.292804C9.89455 0.480344 10 0.734667 10 0.999847C10 1.26503 9.89455 1.51935 9.70685 1.70689L4.70059 6.7072C4.51283 6.89468 4.2582 7 3.9927 7C3.72721 7 3.47258 6.89468 3.28482 6.7072L0.281063 3.70701C0.0986771 3.5184 -0.00224342 3.26578 3.785e-05 3.00357C0.00231912 2.74136 0.10762 2.49053 0.29326 2.30511C0.4789 2.11969 0.730026 2.01451 0.992551 2.01224C1.25508 2.00996 1.50799 2.11076 1.69683 2.29293L3.9927 4.58607L8.29108 0.292804C8.47884 0.105322 8.73347 0 8.99896 0C9.26446 0 9.51908 0.105322 9.70685 0.292804Z"
fill=""
/>
</svg>
</span>
</div>
</div>
<p>Remember me</p>
</label>
<a href="#" className="text-sm text-primary">
Forget password?
</a>
</div>
<button className="flex w-full justify-center rounded bg-primary p-3 font-medium text-gray">
Sign In
</button>
</div>
</form>
</div>
{/* <!-- Sign Up Form --> */}
<div className="rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="border-b border-stroke py-4 px-6.5 dark:border-strokedark">
<h3 className="font-medium text-black dark:text-white">
Sign Up Form
</h3>
</div>
<form action="#">
<div className="p-6.5">
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
Name
</label>
<input
type="text"
placeholder="Enter your full name"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
Email
</label>
<input
type="email"
placeholder="Enter your email address"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div className="mb-4.5">
<label className="mb-2.5 block text-black dark:text-white">
Password
</label>
<input
type="password"
placeholder="Enter password"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<div className="mb-5.5">
<label className="mb-2.5 block text-black dark:text-white">
Re-type Password
</label>
<input
type="password"
placeholder="Re-enter password"
className="w-full rounded border-[1.5px] border-stroke bg-transparent py-3 px-5 font-medium 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"
/>
</div>
<button className="flex w-full justify-center rounded bg-primary p-3 font-medium text-gray">
Sign Up
</button>
</div>
</form>
</div>
</div>
</div>
</DefaultLayout>
);
};
export default FormLayout;

View File

@@ -1,16 +0,0 @@
import Breadcrumb from '../components/Breadcrumb';
import UsersTable from '../components/UsersTable';
import DefaultLayout from '../layout/DefaultLayout';
const Users = () => {
return (
<DefaultLayout>
<Breadcrumb pageName="Users" />
<div className="flex flex-col gap-10">
<UsersTable />
</div>
</DefaultLayout>
);
};
export default Users;

View File

@@ -9,7 +9,7 @@ import logo from '../static/logo.png';
import DropdownUser from '../../user/DropdownUser';
import { UserMenuItems } from '../../user/UserMenuItems';
import { DocsUrl, BlogUrl } from '../../common';
import DarkModeSwitcher from '../admin/components/DarkModeSwitcher';
import DarkModeSwitcher from './DarkModeSwitcher';
const navigation = [
{ name: 'AI Scheduler (Demo App)', href: routes.DemoAppRoute.build() },

View File

@@ -1,5 +1,5 @@
import { cn } from '../../cn';
import useColorMode from '../../hooks/useColorMode';
import { cn } from '../cn';
import useColorMode from '../hooks/useColorMode';
const DarkModeSwitcher = () => {
const [colorMode, setColorMode] = useColorMode();

View File

Before

Width:  |  Height:  |  Size: 493 B

After

Width:  |  Height:  |  Size: 493 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More