mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-11-24 19:17:22 +01:00
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:
@@ -7,8 +7,8 @@
|
||||
+#/--------------------------------------------------/
|
||||
+
|
||||
+# development
|
||||
+DOTENV_VAULT_DEVELOPMENT="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"
|
||||
+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="
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -323,7 +321,10 @@
|
||||
@@ -195,7 +193,10 @@
|
||||
email String? @unique
|
||||
username String? @unique
|
||||
lastActiveTimestamp DateTime @default(now())
|
||||
|
||||
@@ -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') {
|
||||
@@ -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'>
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||

|
||||
|
||||
---
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }) => {
|
||||
@@ -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;
|
||||
@@ -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: {
|
||||
@@ -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;
|
||||
@@ -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(() => {
|
||||
@@ -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(() => {
|
||||
@@ -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(() => {
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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 &&
|
||||
@@ -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 (
|
||||
23
template/app/src/admin/elements/charts/ChartsPage.tsx
Normal file
23
template/app/src/admin/elements/charts/ChartsPage.tsx
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
271
template/app/src/admin/elements/forms/FormElementsPage.tsx
Normal file
271
template/app/src/admin/elements/forms/FormElementsPage.tsx
Normal 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;
|
||||
226
template/app/src/admin/elements/forms/FormLayoutsPage.tsx
Normal file
226
template/app/src/admin/elements/forms/FormLayoutsPage.tsx
Normal 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;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { cn } from '../../cn';
|
||||
import { cn } from '../../../client/cn';
|
||||
|
||||
const SwitcherTwo = () => {
|
||||
const [enabled, setEnabled] = useState(false);
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
@@ -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 = () => {
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
38
template/app/src/analytics/operations.ts
Normal file
38
template/app/src/analytics/operations.ts
Normal 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 };
|
||||
};
|
||||
@@ -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) {
|
||||
@@ -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());
|
||||
@@ -1,3 +0,0 @@
|
||||
import { type DailyStats } from 'wasp/entities';
|
||||
|
||||
export type DailyStatsProps = { dailyStats?: DailyStats; weeklyStats?: DailyStats[]; isLoading?: boolean };
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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() },
|
||||
|
||||
@@ -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();
|
||||
|
Before Width: | Height: | Size: 493 B After Width: | Height: | Size: 493 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
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
Reference in New Issue
Block a user