diff --git a/jsconfig.json b/jsconfig.json deleted file mode 100644 index b30b1c0f..00000000 --- a/jsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["src/*"], - }, - }, -} \ No newline at end of file diff --git a/package.json b/package.json index 44f14fa6..d156c3ce 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "prettier": "3.0.3", "prettier-plugin-tailwindcss": "0.5.6", "tailwindcss": "3.3.4", + "typescript": "^5.8.3", "vite": "4.5.0" } -} \ No newline at end of file +} diff --git a/src/App.jsx b/src/App.tsx similarity index 100% rename from src/App.jsx rename to src/App.tsx diff --git a/src/configs/charts-config.js b/src/configs/charts-config.js deleted file mode 100644 index 7c2676dd..00000000 --- a/src/configs/charts-config.js +++ /dev/null @@ -1,61 +0,0 @@ -export const chartsConfig = { - chart: { - toolbar: { - show: false, - }, - }, - title: { - show: "", - }, - dataLabels: { - enabled: false, - }, - xaxis: { - axisTicks: { - show: false, - }, - axisBorder: { - show: false, - }, - labels: { - style: { - colors: "#37474f", - fontSize: "13px", - fontFamily: "inherit", - fontWeight: 300, - }, - }, - }, - yaxis: { - labels: { - style: { - colors: "#37474f", - fontSize: "13px", - fontFamily: "inherit", - fontWeight: 300, - }, - }, - }, - grid: { - show: true, - borderColor: "#dddddd", - strokeDashArray: 5, - xaxis: { - lines: { - show: true, - }, - }, - padding: { - top: 5, - right: 20, - }, - }, - fill: { - opacity: 0.8, - }, - tooltip: { - theme: "dark", - }, -}; - -export default chartsConfig; diff --git a/src/configs/charts-config.ts b/src/configs/charts-config.ts new file mode 100644 index 00000000..5f54193a --- /dev/null +++ b/src/configs/charts-config.ts @@ -0,0 +1,124 @@ +export interface ChartsConfig { // Add export here + chart: { + toolbar: { + show: boolean; + }; + }; + title: { + show: string; // Or boolean, if it's meant to be a toggle + }; + dataLabels: { + enabled: boolean; + }; + xaxis: { + axisTicks: { + show: boolean; + }; + axisBorder: { + show: boolean; + }; + labels: { + style: { + colors: string; + fontSize: string; + fontFamily: string; + fontWeight: number; + }; + }; + }; + yaxis: { + labels: { + style: { + colors: string; + fontSize: string; + fontFamily: string; + fontWeight: number; + }; + }; + }; + grid: { + show: boolean; + borderColor: string; + strokeDashArray: number; + xaxis: { + lines: { + show: boolean; + }; + }; + padding: { + top: number; + right: number; + }; + }; + fill: { + opacity: number; + }; + tooltip: { + theme: string; // Could be "dark" | "light" or other specific theme names + }; +} + +export const chartsConfig: ChartsConfig = { + chart: { + toolbar: { + show: false, + }, + }, + title: { + show: "", + }, + dataLabels: { + enabled: false, + }, + xaxis: { + axisTicks: { + show: false, + }, + axisBorder: { + show: false, + }, + labels: { + style: { + colors: "#37474f", + fontSize: "13px", + fontFamily: "inherit", + fontWeight: 300, + }, + }, + }, + yaxis: { + labels: { + style: { + colors: "#37474f", + fontSize: "13px", + fontFamily: "inherit", + fontWeight: 300, + }, + }, + }, + grid: { + show: true, + borderColor: "#dddddd", + strokeDashArray: 5, + xaxis: { + lines: { + show: true, + }, + }, + padding: { + top: 5, + right: 20, + }, + }, + fill: { + opacity: 0.8, + }, + tooltip: { + theme: "dark", + }, +}; + +// As chartsConfig is already exported as a named export, +// the default export might be redundant unless specifically used elsewhere. +// For now, I'll keep it as it was. +export default chartsConfig; diff --git a/src/configs/index.js b/src/configs/index.ts similarity index 100% rename from src/configs/index.js rename to src/configs/index.ts diff --git a/src/context/index.jsx b/src/context/index.jsx deleted file mode 100644 index 653a362d..00000000 --- a/src/context/index.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -export const MaterialTailwind = React.createContext(null); -MaterialTailwind.displayName = "MaterialTailwindContext"; - -export function reducer(state, action) { - switch (action.type) { - case "OPEN_SIDENAV": { - return { ...state, openSidenav: action.value }; - } - case "SIDENAV_TYPE": { - return { ...state, sidenavType: action.value }; - } - case "SIDENAV_COLOR": { - return { ...state, sidenavColor: action.value }; - } - case "TRANSPARENT_NAVBAR": { - return { ...state, transparentNavbar: action.value }; - } - case "FIXED_NAVBAR": { - return { ...state, fixedNavbar: action.value }; - } - case "OPEN_CONFIGURATOR": { - return { ...state, openConfigurator: action.value }; - } - default: { - throw new Error(`Unhandled action type: ${action.type}`); - } - } -} - -export function MaterialTailwindControllerProvider({ children }) { - const initialState = { - openSidenav: false, - sidenavColor: "dark", - sidenavType: "white", - transparentNavbar: true, - fixedNavbar: false, - openConfigurator: false, - }; - - const [controller, dispatch] = React.useReducer(reducer, initialState); - const value = React.useMemo( - () => [controller, dispatch], - [controller, dispatch] - ); - - return ( - - {children} - - ); -} - -export function useMaterialTailwindController() { - const context = React.useContext(MaterialTailwind); - - if (!context) { - throw new Error( - "useMaterialTailwindController should be used inside the MaterialTailwindControllerProvider." - ); - } - - return context; -} - -MaterialTailwindControllerProvider.displayName = "/src/context/index.jsx"; - -MaterialTailwindControllerProvider.propTypes = { - children: PropTypes.node.isRequired, -}; - -export const setOpenSidenav = (dispatch, value) => - dispatch({ type: "OPEN_SIDENAV", value }); -export const setSidenavType = (dispatch, value) => - dispatch({ type: "SIDENAV_TYPE", value }); -export const setSidenavColor = (dispatch, value) => - dispatch({ type: "SIDENAV_COLOR", value }); -export const setTransparentNavbar = (dispatch, value) => - dispatch({ type: "TRANSPARENT_NAVBAR", value }); -export const setFixedNavbar = (dispatch, value) => - dispatch({ type: "FIXED_NAVBAR", value }); -export const setOpenConfigurator = (dispatch, value) => - dispatch({ type: "OPEN_CONFIGURATOR", value }); diff --git a/src/context/index.tsx b/src/context/index.tsx new file mode 100644 index 00000000..142e9b56 --- /dev/null +++ b/src/context/index.tsx @@ -0,0 +1,125 @@ +import React, { createContext, useContext, useReducer, useMemo, Dispatch, ReactNode } from "react"; + +// Define types used by the context state and actions +export type SidenavColor = "white" | "dark" | "green" | "orange" | "red" | "pink" | "blue"; +export type SidenavType = "dark" | "white" | "transparent"; + +// Interface for the context state +export interface MaterialTailwindState { + openSidenav: boolean; + sidenavColor: SidenavColor; + sidenavType: SidenavType; + transparentNavbar: boolean; + fixedNavbar: boolean; + openConfigurator: boolean; +} + +// Interface for reducer actions +export type MaterialTailwindAction = + | { type: "OPEN_SIDENAV"; value: boolean } + | { type: "SIDENAV_TYPE"; value: SidenavType } + | { type: "SIDENAV_COLOR"; value: SidenavColor } + | { type: "TRANSPARENT_NAVBAR"; value: boolean } + | { type: "FIXED_NAVBAR"; value: boolean } + | { type: "OPEN_CONFIGURATOR"; value: boolean }; + +// Type for the context value: [state, dispatch] +// Initialize with null, as per original createContext(null) +type MaterialTailwindContextType = [MaterialTailwindState, Dispatch] | null; + +export const MaterialTailwind = createContext(null); +MaterialTailwind.displayName = "MaterialTailwindContext"; + +export function reducer(state: MaterialTailwindState, action: MaterialTailwindAction): MaterialTailwindState { + switch (action.type) { + case "OPEN_SIDENAV": { + return { ...state, openSidenav: action.value }; + } + case "SIDENAV_TYPE": { + return { ...state, sidenavType: action.value }; + } + case "SIDENAV_COLOR": { + return { ...state, sidenavColor: action.value }; + } + case "TRANSPARENT_NAVBAR": { + return { ...state, transparentNavbar: action.value }; + } + case "FIXED_NAVBAR": { + return { ...state, fixedNavbar: action.value }; + } + case "OPEN_CONFIGURATOR": { + return { ...state, openConfigurator: action.value }; + } + default: { + // Ensure exhaustive check for actions if needed, or handle unknown actions + // For example, by asserting 'never' for the action type in default + // const _exhaustiveCheck: never = action; + throw new Error(`Unhandled action type: ${(action as MaterialTailwindAction).type}`); + } + } +} + +// Props for the provider component +interface MaterialTailwindControllerProviderProps { + children: ReactNode; +} + +export function MaterialTailwindControllerProvider({ children }: MaterialTailwindControllerProviderProps) { + const initialState: MaterialTailwindState = { + openSidenav: false, + sidenavColor: "dark", // Default color + sidenavType: "white", // Default type + transparentNavbar: true, + fixedNavbar: false, + openConfigurator: false, + }; + + const [controller, dispatch] = useReducer(reducer, initialState); + + // Memoize the context value + const value: [MaterialTailwindState, Dispatch] = useMemo( + () => [controller, dispatch], + [controller, dispatch] + ); + + return ( + + {children} + + ); +} + +export function useMaterialTailwindController(): [MaterialTailwindState, Dispatch] { + const context = useContext(MaterialTailwind); + + if (!context) { + throw new Error( + "useMaterialTailwindController should be used inside the MaterialTailwindControllerProvider." + ); + } + + return context; +} + +MaterialTailwindControllerProvider.displayName = "/src/context/index.tsx"; // Updated display name + +// PropTypes are removed as types are handled by TypeScript + +// Typed action creator functions +export const setOpenSidenav = (dispatch: Dispatch, value: boolean) => + dispatch({ type: "OPEN_SIDENAV", value }); + +export const setSidenavType = (dispatch: Dispatch, value: SidenavType) => + dispatch({ type: "SIDENAV_TYPE", value }); + +export const setSidenavColor = (dispatch: Dispatch, value: SidenavColor) => + dispatch({ type: "SIDENAV_COLOR", value }); + +export const setTransparentNavbar = (dispatch: Dispatch, value: boolean) => + dispatch({ type: "TRANSPARENT_NAVBAR", value }); + +export const setFixedNavbar = (dispatch: Dispatch, value: boolean) => + dispatch({ type: "FIXED_NAVBAR", value }); + +export const setOpenConfigurator = (dispatch: Dispatch, value: boolean) => + dispatch({ type: "OPEN_CONFIGURATOR", value }); diff --git a/src/data/authors-table-data.js b/src/data/authors-table-data.ts similarity index 80% rename from src/data/authors-table-data.js rename to src/data/authors-table-data.ts index 7ae9d9cb..c9cf66af 100644 --- a/src/data/authors-table-data.js +++ b/src/data/authors-table-data.ts @@ -1,4 +1,13 @@ -export const authorsTableData = [ +interface Author { + img: string; + name: string; + email: string; + job: [string, string]; // Tuple for job title and department + online: boolean; + date: string; // Assuming date is always a string in "dd/mm/yy" format +} + +export const authorsTableData: Author[] = [ { img: "/img/team-2.jpeg", name: "John Michael", diff --git a/src/data/conversations-data.js b/src/data/conversations-data.ts similarity index 81% rename from src/data/conversations-data.js rename to src/data/conversations-data.ts index 2b488644..31764f0f 100644 --- a/src/data/conversations-data.js +++ b/src/data/conversations-data.ts @@ -1,4 +1,10 @@ -export const conversationsData = [ +interface Conversation { + img: string; + name: string; + message: string; +} + +export const conversationsData: Conversation[] = [ { img: "/img/team-1.jpeg", name: "Sophie B.", diff --git a/src/data/index.js b/src/data/index.ts similarity index 100% rename from src/data/index.js rename to src/data/index.ts diff --git a/src/data/orders-overview-data.js b/src/data/orders-overview-data.ts similarity index 76% rename from src/data/orders-overview-data.js rename to src/data/orders-overview-data.ts index d1ba49d9..a2d80d6f 100644 --- a/src/data/orders-overview-data.js +++ b/src/data/orders-overview-data.ts @@ -6,8 +6,16 @@ import { LockOpenIcon, BanknotesIcon, } from "@heroicons/react/24/solid"; +import { ForwardRefExoticComponent, SVGProps } from 'react'; -export const ordersOverviewData = [ +interface OrderOverviewItem { + icon: ForwardRefExoticComponent, "ref"> & { title?: string, titleId?: string }>; + color: string; + title: string; + description: string; +} + +export const ordersOverviewData: OrderOverviewItem[] = [ { icon: BellIcon, color: "text-blue-gray-300", diff --git a/src/data/platform-settings-data.js b/src/data/platform-settings-data.ts similarity index 76% rename from src/data/platform-settings-data.js rename to src/data/platform-settings-data.ts index 140c5dfd..de9c3976 100644 --- a/src/data/platform-settings-data.js +++ b/src/data/platform-settings-data.ts @@ -1,4 +1,14 @@ -export const platformSettingsData = [ +interface PlatformSettingOption { + checked: boolean; + label: string; +} + +interface PlatformSetting { + title: string; + options: PlatformSettingOption[]; +} + +export const platformSettingsData: PlatformSetting[] = [ { title: "account", options: [ diff --git a/src/data/projects-data.js b/src/data/projects-data.ts similarity index 88% rename from src/data/projects-data.js rename to src/data/projects-data.ts index 3c83bc08..945b323d 100644 --- a/src/data/projects-data.js +++ b/src/data/projects-data.ts @@ -1,4 +1,18 @@ -export const projectsData = [ +interface ProjectMember { + img: string; + name: string; +} + +interface Project { + img: string; + title: string; + tag: string; + description: string; + route: string; + members: ProjectMember[]; +} + +export const projectsData: Project[] = [ { img: "/img/home-decor-1.jpeg", title: "Modern", diff --git a/src/data/projects-table-data.js b/src/data/projects-table-data.ts similarity index 87% rename from src/data/projects-table-data.js rename to src/data/projects-table-data.ts index d37e08ba..435bb18e 100644 --- a/src/data/projects-table-data.js +++ b/src/data/projects-table-data.ts @@ -1,4 +1,17 @@ -export const projectsTableData = [ +interface ProjectTableMember { + img: string; + name: string; +} + +interface ProjectTableRow { + img: string; + name: string; + members: ProjectTableMember[]; + budget: string; + completion: number; +} + +export const projectsTableData: ProjectTableRow[] = [ { img: "/img/logo-xd.svg", name: "Material XD Version", diff --git a/src/data/statistics-cards-data.js b/src/data/statistics-cards-data.ts similarity index 68% rename from src/data/statistics-cards-data.js rename to src/data/statistics-cards-data.ts index d470e708..e485ae49 100644 --- a/src/data/statistics-cards-data.js +++ b/src/data/statistics-cards-data.ts @@ -4,8 +4,23 @@ import { UsersIcon, ChartBarIcon, } from "@heroicons/react/24/solid"; +import { ForwardRefExoticComponent, SVGProps } from 'react'; -export const statisticsCardsData = [ +interface StatisticsCardFooter { + color: string; + value: string; + label: string; +} + +interface StatisticsCard { + color: string; + icon: ForwardRefExoticComponent, "ref"> & { title?: string, titleId?: string }>; + title: string; + value: string; + footer: StatisticsCardFooter; +} + +export const statisticsCardsData: StatisticsCard[] = [ { color: "gray", icon: BanknotesIcon, diff --git a/src/data/statistics-charts-data.js b/src/data/statistics-charts-data.js deleted file mode 100644 index 15a36464..00000000 --- a/src/data/statistics-charts-data.js +++ /dev/null @@ -1,131 +0,0 @@ -import { chartsConfig } from "@/configs"; - -const websiteViewsChart = { - type: "bar", - height: 220, - series: [ - { - name: "Views", - data: [50, 20, 10, 22, 50, 10, 40], - }, - ], - options: { - ...chartsConfig, - colors: "#388e3c", - plotOptions: { - bar: { - columnWidth: "16%", - borderRadius: 5, - }, - }, - xaxis: { - ...chartsConfig.xaxis, - categories: ["M", "T", "W", "T", "F", "S", "S"], - }, - }, -}; - -const dailySalesChart = { - type: "line", - height: 220, - series: [ - { - name: "Sales", - data: [50, 40, 300, 320, 500, 350, 200, 230, 500], - }, - ], - options: { - ...chartsConfig, - colors: ["#0288d1"], - stroke: { - lineCap: "round", - }, - markers: { - size: 5, - }, - xaxis: { - ...chartsConfig.xaxis, - categories: [ - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ], - }, - }, -}; - -const completedTaskChart = { - type: "line", - height: 220, - series: [ - { - name: "Sales", - data: [50, 40, 300, 320, 500, 350, 200, 230, 500], - }, - ], - options: { - ...chartsConfig, - colors: ["#388e3c"], - stroke: { - lineCap: "round", - }, - markers: { - size: 5, - }, - xaxis: { - ...chartsConfig.xaxis, - categories: [ - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ], - }, - }, -}; -const completedTasksChart = { - ...completedTaskChart, - series: [ - { - name: "Tasks", - data: [50, 40, 300, 220, 500, 250, 400, 230, 500], - }, - ], -}; - -export const statisticsChartsData = [ - { - color: "white", - title: "Website View", - description: "Last Campaign Performance", - footer: "campaign sent 2 days ago", - chart: websiteViewsChart, - }, - { - color: "white", - title: "Daily Sales", - description: "15% increase in today sales", - footer: "updated 4 min ago", - chart: dailySalesChart, - }, - { - color: "white", - title: "Completed Tasks", - description: "Last Campaign Performance", - footer: "just updated", - chart: completedTasksChart, - }, -]; - -export default statisticsChartsData; diff --git a/src/data/statistics-charts-data.ts b/src/data/statistics-charts-data.ts new file mode 100644 index 00000000..f445aab2 --- /dev/null +++ b/src/data/statistics-charts-data.ts @@ -0,0 +1,203 @@ +import { chartsConfig } from "@/configs"; +import type { ChartsConfig } from "@/configs"; // Type-only import for ChartsConfig + +// Interfaces for statistics charts +interface ChartSeries { + name: string; + data: number[]; +} + +interface SpecificChartOptions extends Omit { // Omit and redefine if types differ significantly or are always present + colors: string | string[]; // chartsConfig might not have colors, or it might, this makes it explicit + plotOptions?: { + bar?: { + columnWidth?: string; + borderRadius?: number; + }; + }; + xaxis: ChartsConfig["xaxis"] & { + categories: string[]; + }; + stroke?: { + lineCap?: "butt" | "square" | "round"; + }; + markers?: { + size?: number; + }; + // Ensure all properties from chartsConfig are potentially available if not redefined + // This can be complex. A simpler approach might be Partial & { specific overrides } + // For now, let's stick to this approach assuming chartsConfig provides a base. +} + + +// Simpler SpecificChartOptions that directly uses ChartsConfig as base +// This assumes that 'colors' and 'xaxis.categories' are being ADDED to what chartsConfig provides, +// or are compatible with chartsConfig's types if they exist there. +interface BetterSpecificChartOptions extends ChartsConfig { + colors: string | string[]; // If chartsConfig.colors exists, its type must be compatible. + plotOptions?: { + bar?: { + columnWidth?: string; + borderRadius?: number; + }; + }; + xaxis: ChartsConfig["xaxis"] & { // This is good, extends specific part of ChartsConfig + categories: string[]; + }; + stroke?: { + lineCap?: "butt" | "square" | "round"; + }; + markers?: { + size?: number; + }; +} + + +interface ChartData { + type: "bar" | "line"; + height: number; + series: ChartSeries[]; + options: BetterSpecificChartOptions; // Using the refined options type +} + +interface StatisticsChart { + color: string; + title: string; + description: string; + footer: string; + chart: ChartData; +} + +const websiteViewsChart: ChartData = { + type: "bar", + height: 220, + series: [ + { + name: "Views", + data: [50, 20, 10, 22, 50, 10, 40], + }, + ], + options: { + ...chartsConfig, + colors: "#388e3c", // This now fits BetterSpecificChartOptions.colors + plotOptions: { + bar: { + columnWidth: "16%", + borderRadius: 5, + }, + }, + xaxis: { + ...chartsConfig.xaxis, + categories: ["M", "T", "W", "T", "F", "S", "S"], + }, + }, +}; + +const dailySalesChart: ChartData = { + type: "line", + height: 220, + series: [ + { + name: "Sales", + data: [50, 40, 300, 320, 500, 350, 200, 230, 500], + }, + ], + options: { + ...chartsConfig, + colors: ["#0288d1"], // This now fits BetterSpecificChartOptions.colors (string[]) + stroke: { + lineCap: "round", + }, + markers: { + size: 5, + }, + xaxis: { + ...chartsConfig.xaxis, + categories: [ + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ], + }, + }, +}; + +// completedTaskChart is not directly used in the final export, +// but completedTasksChart is based on it. So, it should also be typed. +const completedTaskChart: ChartData = { + type: "line", + height: 220, + series: [ + { + name: "Sales", + data: [50, 40, 300, 320, 500, 350, 200, 230, 500], + }, + ], + options: { + ...chartsConfig, + colors: ["#388e3c"], // This now fits BetterSpecificChartOptions.colors (string[]) + stroke: { + lineCap: "round", + }, + markers: { + size: 5, + }, + xaxis: { + ...chartsConfig.xaxis, + categories: [ + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ], + }, + }, +}; +// Type for completedTasksChart. It's ChartData but with a specific series. +// The spread from completedTaskChart (which is ChartData) means it largely conforms. +const completedTasksChart: ChartData = { + ...completedTaskChart, + series: [ // This overrides completedTaskChart.series + { + name: "Tasks", + data: [50, 40, 300, 220, 500, 250, 400, 230, 500], + }, + ], +}; + +export const statisticsChartsData: StatisticsChart[] = [ + { + color: "white", + title: "Website View", + description: "Last Campaign Performance", + footer: "campaign sent 2 days ago", + chart: websiteViewsChart, + }, + { + color: "white", + title: "Daily Sales", + description: "15% increase in today sales", + footer: "updated 4 min ago", + chart: dailySalesChart, + }, + { + color: "white", + title: "Completed Tasks", + description: "Last Campaign Performance", + footer: "just updated", + chart: completedTasksChart, + }, +]; + +export default statisticsChartsData; diff --git a/src/layouts/auth.jsx b/src/layouts/auth.jsx deleted file mode 100644 index c7a21165..00000000 --- a/src/layouts/auth.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Routes, Route } from "react-router-dom"; -import { - ChartPieIcon, - UserIcon, - UserPlusIcon, - ArrowRightOnRectangleIcon, -} from "@heroicons/react/24/solid"; -import { Navbar, Footer } from "@/widgets/layout"; -import routes from "@/routes"; - -export function Auth() { - const navbarRoutes = [ - { - name: "dashboard", - path: "/dashboard/home", - icon: ChartPieIcon, - }, - { - name: "profile", - path: "/dashboard/home", - icon: UserIcon, - }, - { - name: "sign up", - path: "/auth/sign-up", - icon: UserPlusIcon, - }, - { - name: "sign in", - path: "/auth/sign-in", - icon: ArrowRightOnRectangleIcon, - }, - ]; - - return ( -
- - {routes.map( - ({ layout, pages }) => - layout === "auth" && - pages.map(({ path, element }) => ( - - )) - )} - -
- ); -} - -Auth.displayName = "/src/layout/Auth.jsx"; - -export default Auth; diff --git a/src/layouts/auth.tsx b/src/layouts/auth.tsx new file mode 100644 index 00000000..b9e4c158 --- /dev/null +++ b/src/layouts/auth.tsx @@ -0,0 +1,88 @@ +import { Routes, Route } from "react-router-dom"; +import { + ChartPieIcon, + UserIcon, + UserPlusIcon, + ArrowRightOnRectangleIcon, +} from "@heroicons/react/24/solid"; +import { Navbar, Footer } from "@/widgets/layout"; // These will be typed later +import routes from "@/routes"; // This will be typed when @/routes is migrated + +import { ForwardRefExoticComponent, SVGProps, ReactNode } from 'react'; // Added ReactNode for element + +// Type for Heroicon components +type HeroIconType = ForwardRefExoticComponent, "ref"> & { title?: string, titleId?: string }>; + +interface NavbarRoute { + name: string; + path: string; + icon: HeroIconType; +} + +// Tentative types for the imported 'routes' structure +interface PageConfig { + path: string; + element: ReactNode; // JSX.Element or React.ReactNode should work + name?: string; // Optional, based on common patterns + icon?: HeroIconType | JSX.Element; // Optional +} + +interface RouteConfig { + layout: string; + pages: PageConfig[]; + name?: string; // Optional + icon?: HeroIconType | JSX.Element; // Optional +} + +export function Auth() { + // The 'routes' import is expected to be RouteConfig[] + // For now, we'll assume it matches this structure. + // If not, TypeScript will complain when routes.js is converted or when this is used. + + const navbarRoutes: NavbarRoute[] = [ + { + name: "dashboard", + path: "/dashboard/home", + icon: ChartPieIcon, + }, + { + name: "profile", + path: "/dashboard/home", + icon: UserIcon, + }, + { + name: "sign up", + path: "/auth/sign-up", + icon: UserPlusIcon, + }, + { + name: "sign in", + path: "/auth/sign-in", + icon: ArrowRightOnRectangleIcon, + }, + ]; + + return ( +
+ {/* The Navbar and Footer are commented out in the original auth.jsx, so no props to worry about here yet */} + {/* */} + {/*
*/} + + {(routes as RouteConfig[]).map( // Cast routes to expected type for now + ({ layout, pages }, key: number) => // Added key for map + layout === "auth" && + pages.map(({ path, element }: PageConfig, pageKey: number) => ( // Added key and type for page + // Key added to Route + )) + )} + + {/*
+
+
*/} +
+ ); +} + +Auth.displayName = "/src/layouts/Auth.tsx"; // Updated display name + +export default Auth; diff --git a/src/layouts/dashboard.jsx b/src/layouts/dashboard.jsx deleted file mode 100644 index 888a627a..00000000 --- a/src/layouts/dashboard.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Routes, Route } from "react-router-dom"; -import { Cog6ToothIcon } from "@heroicons/react/24/solid"; -import { IconButton } from "@material-tailwind/react"; -import { - Sidenav, - DashboardNavbar, - Configurator, - Footer, -} from "@/widgets/layout"; -import routes from "@/routes"; -import { useMaterialTailwindController, setOpenConfigurator } from "@/context"; - -export function Dashboard() { - const [controller, dispatch] = useMaterialTailwindController(); - const { sidenavType } = controller; - - return ( -
- -
- - - setOpenConfigurator(dispatch, true)} - > - - - - {routes.map( - ({ layout, pages }) => - layout === "dashboard" && - pages.map(({ path, element }) => ( - - )) - )} - -
-
-
-
-
- ); -} - -Dashboard.displayName = "/src/layout/dashboard.jsx"; - -export default Dashboard; diff --git a/src/layouts/dashboard.tsx b/src/layouts/dashboard.tsx new file mode 100644 index 00000000..2ab12c2b --- /dev/null +++ b/src/layouts/dashboard.tsx @@ -0,0 +1,94 @@ +import { Routes, Route } from "react-router-dom"; +import { Cog6ToothIcon } from "@heroicons/react/24/solid"; +import { IconButton } from "@material-tailwind/react"; +import { + Sidenav, + DashboardNavbar, + Configurator, + Footer, +} from "@/widgets/layout"; // Sidenav, DashboardNavbar etc. will be typed later +import routes from "@/routes"; // Will be typed when @/routes is migrated +import { useMaterialTailwindController, setOpenConfigurator } from "@/context"; // Context functions + +import { ReactNode, Dispatch } from 'react'; // Added for types + +// Tentative types for the imported 'routes' structure (consistent with auth.tsx) +interface PageConfig { + path: string; + element: ReactNode; + name?: string; + icon?: any; // Simplified for now + // other properties if they exist +} + +interface RouteConfig { + layout: string; + pages: PageConfig[]; + name?: string; + icon?: any; // Simplified for now + // other properties if they exist +} + +// Simplified types for MaterialTailwindController context +interface MaterialTailwindState { + sidenavType: "dark" | "white" | string; // Assuming sidenavType can be one of these + openConfigurator: boolean; + // Add other relevant state properties from the context if known + // e.g., fixedNavbar: boolean, sidenavColor: string etc. +} + +// Define action types used by this component or relevant to it +type MaterialTailwindAction = + | { type: "OPEN_CONFIGURATOR"; value: boolean } + | { type: "SIDENAV_TYPE"; value: "dark" | "white" } + // Add other action types if they are used by setOpenConfigurator or affect controller state used here + | { type: string; value?: any }; // Fallback for other actions + +type MaterialTailwindDispatch = Dispatch; + + +export function Dashboard() { + // Assume useMaterialTailwindController returns state and dispatch matching these types + const [controller, dispatch] = useMaterialTailwindController() as [MaterialTailwindState, MaterialTailwindDispatch]; + const { sidenavType } = controller; // sidenavType is now typed + + return ( +
+ +
+ + + setOpenConfigurator(dispatch, true)} // dispatch is now typed + > + + + + {(routes as RouteConfig[]).map( // Cast routes to expected type + ({ layout, pages }, key: number) => // Added key for map + layout === "dashboard" && + pages.map(({ path, element }: PageConfig, pageKey: number) => ( // Added key and type for page + // Key added + )) + )} + +
+
+
+
+
+ ); +} + +Dashboard.displayName = "/src/layouts/dashboard.tsx"; // Updated display name + +export default Dashboard; diff --git a/src/layouts/index.js b/src/layouts/index.ts similarity index 100% rename from src/layouts/index.js rename to src/layouts/index.ts diff --git a/src/main.jsx b/src/main.tsx similarity index 93% rename from src/main.jsx rename to src/main.tsx index a28f5a60..15a25023 100644 --- a/src/main.jsx +++ b/src/main.tsx @@ -17,7 +17,7 @@ import { ThemeProvider } from "@material-tailwind/react"; import { MaterialTailwindControllerProvider } from "@/context"; import "../public/css/tailwind.css"; -ReactDOM.createRoot(document.getElementById("root")).render( +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/src/pages/auth/index.js b/src/pages/auth/index.ts similarity index 100% rename from src/pages/auth/index.js rename to src/pages/auth/index.ts diff --git a/src/pages/auth/sign-in.jsx b/src/pages/auth/sign-in.tsx similarity index 100% rename from src/pages/auth/sign-in.jsx rename to src/pages/auth/sign-in.tsx diff --git a/src/pages/auth/sign-up.jsx b/src/pages/auth/sign-up.tsx similarity index 100% rename from src/pages/auth/sign-up.jsx rename to src/pages/auth/sign-up.tsx diff --git a/src/pages/dashboard/home.jsx b/src/pages/dashboard/home.tsx similarity index 75% rename from src/pages/dashboard/home.jsx rename to src/pages/dashboard/home.tsx index 2c700669..39108f87 100644 --- a/src/pages/dashboard/home.jsx +++ b/src/pages/dashboard/home.tsx @@ -25,18 +25,34 @@ import { projectsTableData, ordersOverviewData, } from "@/data"; +// Import specific types from @/data +import type { + StatisticsCard as StatisticsCardDataType, + StatisticsChart as StatisticsChartDataType, + ProjectTableRow, + ProjectTableMember, // Assuming this is the type for members array elements + OrderOverviewItem, + // HeroIconType is also needed if not defined locally, but it's part of StatisticsCardDataType etc. +} from "@/data"; + import { CheckCircleIcon, ClockIcon } from "@heroicons/react/24/solid"; +import type { ForwardRefExoticComponent, SVGProps } from 'react'; // For HeroIconType if needed explicitly + +// If HeroIconType is not exported from data files, define it (it's part of those data types) +type HeroIconType = ForwardRefExoticComponent, "ref"> & { title?: string, titleId?: string }>; + export function Home() { return (
- {statisticsCardsData.map(({ icon, title, footer, ...rest }) => ( + {/* Type the item from statisticsCardsData */} + {(statisticsCardsData as StatisticsCardDataType[]).map(({ icon, title, footer, ...rest }: StatisticsCardDataType) => (
- {statisticsChartsData.map((props) => ( + {/* Type the props from statisticsChartsData */} + {(statisticsChartsData as StatisticsChartDataType[]).map((props: StatisticsChartDataType) => ( + {/* el is string, key is string, this is fine */} {["companies", "members", "budget", "completion"].map( - (el) => ( + (el: string) => ( - {projectsTableData.map( - ({ img, name, members, budget, completion }, key) => { + {/* Type the item from projectsTableData, note: key here is the index from map */} + {(projectsTableData as ProjectTableRow[]).map( + ({ img, name, members, budget, completion }: ProjectTableRow, index: number) => { const className = `py-3 px-5 ${ - key === projectsTableData.length - 1 + index === projectsTableData.length - 1 // Use index from map for comparison ? "" : "border-b border-blue-gray-50" }`; @@ -147,15 +166,16 @@ export function Home() {
- {members.map(({ img, name }, key) => ( - + {/* Type the member item */} + {(members as ProjectTableMember[]).map(({ img: memberImg, name: memberName }, memberKey: number) => ( + @@ -178,7 +198,7 @@ export function Home() { {completion}% - {ordersOverviewData.map( - ({ icon, color, title, description }, key) => ( + {/* Type the item from ordersOverviewData, note: key here is the index from map */} + {(ordersOverviewData as OrderOverviewItem[]).map( + ({ icon, color, title, description }: OrderOverviewItem, index: number) => (
- {React.createElement(icon, { + {React.createElement(icon as HeroIconType, { // Cast icon to HeroIconType className: `!w-5 !h-5 ${color}`, })}
diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.ts similarity index 100% rename from src/pages/dashboard/index.js rename to src/pages/dashboard/index.ts diff --git a/src/pages/dashboard/notifications.jsx b/src/pages/dashboard/notifications.tsx similarity index 59% rename from src/pages/dashboard/notifications.jsx rename to src/pages/dashboard/notifications.tsx index f4be88b0..1405430e 100644 --- a/src/pages/dashboard/notifications.jsx +++ b/src/pages/dashboard/notifications.tsx @@ -8,20 +8,31 @@ import { } from "@material-tailwind/react"; import { InformationCircleIcon } from "@heroicons/react/24/outline"; +// Define types for state and alert colors +type AlertColor = "gray" | "green" | "orange" | "red"; // Add other Material Tailwind colors if used + +interface AlertVisibilityState { + gray: boolean; + green: boolean; + orange: boolean; + red: boolean; + // Potentially other colors if needed, ensure this matches 'alerts' array +} + export function Notifications() { - const [showAlerts, setShowAlerts] = React.useState({ - blue: true, + const [showAlerts, setShowAlerts] = React.useState({ + gray: true, // Changed from blue to gray to match 'alerts' array green: true, orange: true, red: true, }); - const [showAlertsWithIcon, setShowAlertsWithIcon] = React.useState({ - blue: true, + const [showAlertsWithIcon, setShowAlertsWithIcon] = React.useState({ + gray: true, // Changed from blue to gray to match 'alerts' array green: true, orange: true, red: true, }); - const alerts = ["gray", "green", "orange", "red"]; + const alerts: AlertColor[] = ["gray", "green", "orange", "red"]; return (
@@ -37,12 +48,12 @@ export function Notifications() { - {alerts.map((color) => ( + {alerts.map((color: AlertColor) => ( // Explicitly type color setShowAlerts((current) => ({ ...current, [color]: false }))} + open={showAlerts[color]} // color is a valid key of AlertVisibilityState + color={color} // color is a valid AlertColor + onClose={() => setShowAlerts((current: AlertVisibilityState) => ({ ...current, [color]: false }))} > A simple {color} alert with an example link. Give it a click if you like. @@ -62,15 +73,15 @@ export function Notifications() { - {alerts.map((color) => ( + {alerts.map((color: AlertColor) => ( // Explicitly type color } - onClose={() => setShowAlertsWithIcon((current) => ({ + onClose={() => setShowAlertsWithIcon((current: AlertVisibilityState) => ({ ...current, [color]: false, }))} diff --git a/src/pages/dashboard/profile.jsx b/src/pages/dashboard/profile.tsx similarity index 79% rename from src/pages/dashboard/profile.jsx rename to src/pages/dashboard/profile.tsx index 0d9f0115..2f7fe449 100644 --- a/src/pages/dashboard/profile.jsx +++ b/src/pages/dashboard/profile.tsx @@ -19,9 +19,18 @@ import { PencilIcon, } from "@heroicons/react/24/solid"; import { Link } from "react-router-dom"; -import { ProfileInfoCard, MessageCard } from "@/widgets/cards"; +import { ProfileInfoCard, MessageCard } from "@/widgets/cards"; // Widgets to be typed later import { platformSettingsData, conversationsData, projectsData } from "@/data"; +// Import types from @/data +import type { + PlatformSetting, + PlatformSettingOption, + Conversation as ConversationDataType, + Project as ProjectDataType, + ProjectMember, +} from "@/data"; + export function Profile() { return ( <> @@ -76,18 +85,20 @@ export function Profile() { Platform Settings
- {platformSettingsData.map(({ title, options }) => ( + {/* Type platformSettingsData items */} + {(platformSettingsData as PlatformSetting[]).map(({ title, options }: PlatformSetting) => (
{title}
- {options.map(({ checked, label }) => ( + {/* Type options items */} + {(options as PlatformSettingOption[]).map(({ checked, label }: PlatformSettingOption) => (
    - {conversationsData.map((props) => ( + {/* Type conversationsData items (props for MessageCard) */} + {(conversationsData as ConversationDataType[]).map((props: ConversationDataType) => ( reply @@ -150,8 +162,9 @@ export function Profile() { Architects design houses
    - {projectsData.map( - ({ img, title, description, tag, route, members }) => ( + {/* Type projectsData items */} + {(projectsData as ProjectDataType[]).map( + ({ img, title, description, tag, route, members }: ProjectDataType) => ( {title} @@ -169,38 +182,39 @@ export function Profile() { variant="small" className="font-normal text-blue-gray-500" > - {tag} + {tag} {/* tag is string */} - {title} + {title} {/* title is string */} - {description} + {description} {/* description is string */} - + {/* route is string */}
    - {members.map(({ img, name }, key) => ( - + {/* Type members items, key is memberKey from map */} + {(members as ProjectMember[]).map(({ img: memberImg, name: memberName }: ProjectMember, memberKey: number) => ( + diff --git a/src/pages/dashboard/tables.jsx b/src/pages/dashboard/tables.tsx similarity index 82% rename from src/pages/dashboard/tables.jsx rename to src/pages/dashboard/tables.tsx index 3d453ed7..18b4b60b 100644 --- a/src/pages/dashboard/tables.jsx +++ b/src/pages/dashboard/tables.tsx @@ -11,6 +11,13 @@ import { import { EllipsisVerticalIcon } from "@heroicons/react/24/outline"; import { authorsTableData, projectsTableData } from "@/data"; +// Import types from @/data +import type { + Author, + ProjectTableRow, + ProjectTableMember, +} from "@/data"; + export function Tables() { return (
    @@ -40,10 +47,11 @@ export function Tables() { - {authorsTableData.map( - ({ img, name, email, job, online, date }, key) => { + {/* Type authorsTableData items, key is index from map */} + {(authorsTableData as Author[]).map( + ({ img, name, email, job, online, date }: Author, index: number) => { const className = `py-3 px-5 ${ - key === authorsTableData.length - 1 + index === authorsTableData.length - 1 // Use index from map ? "" : "border-b border-blue-gray-50" }`; @@ -69,23 +77,23 @@ export function Tables() { - {job[0]} + {job[0]} {/* job is [string, string] */} - {job[1]} + {job[1]} {/* job is [string, string] */} - {date} + {date} {/* date is string */} @@ -133,10 +141,11 @@ export function Tables() { - {projectsTableData.map( - ({ img, name, members, budget, completion }, key) => { + {/* Type projectsTableData items, key is index from map */} + {(projectsTableData as ProjectTableRow[]).map( + ({ img, name, members, budget, completion }: ProjectTableRow, index: number) => { const className = `py-3 px-5 ${ - key === projectsTableData.length - 1 + index === projectsTableData.length - 1 // Use index from map ? "" : "border-b border-blue-gray-50" }`; @@ -156,15 +165,16 @@ export function Tables() {
    - {members.map(({ img, name }, key) => ( - + {/* Type members items, memberKey is index from this inner map */} + {(members as ProjectTableMember[]).map(({ img: memberImg, name: memberName }: ProjectTableMember, memberKey: number) => ( + @@ -175,7 +185,7 @@ export function Tables() { variant="small" className="text-xs font-medium text-blue-gray-600" > - {budget} + {budget} {/* budget is string */} @@ -184,10 +194,10 @@ export function Tables() { variant="small" className="mb-1 block text-xs font-medium text-blue-gray-600" > - {completion}% + {completion}% {/* completion is number */} + name: string; + path: string; + element: ReactNode; // Components like are ReactNode or JSX.Element +} + +interface RouteGroup { + layout: string; + pages: PageRoute[]; + title?: string; // title is optional +} + +const icon = { // This object is spread onto Heroicon components className: "w-5 h-5 text-inherit", }; -export const routes = [ +// Apply the RouteGroup[] type to the routes array +export const routes: RouteGroup[] = [ { layout: "dashboard", pages: [ diff --git a/src/widgets/cards/index.js b/src/widgets/cards/index.ts similarity index 100% rename from src/widgets/cards/index.js rename to src/widgets/cards/index.ts diff --git a/src/widgets/cards/message-card.jsx b/src/widgets/cards/message-card.jsx deleted file mode 100644 index 5a47ba56..00000000 --- a/src/widgets/cards/message-card.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import PropTypes from "prop-types"; -import { Avatar, Typography } from "@material-tailwind/react"; - -export function MessageCard({ img, name, message, action }) { - return ( -
    -
    - -
    - - {name} - - - {message} - -
    -
    - {action} -
    - ); -} - -MessageCard.defaultProps = { - action: null, -}; - -MessageCard.propTypes = { - img: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - message: PropTypes.node.isRequired, - action: PropTypes.node, -}; - -MessageCard.displayName = "/src/widgets/cards/message-card.jsx"; - -export default MessageCard; diff --git a/src/widgets/cards/message-card.tsx b/src/widgets/cards/message-card.tsx new file mode 100644 index 00000000..5f338820 --- /dev/null +++ b/src/widgets/cards/message-card.tsx @@ -0,0 +1,55 @@ +import { Avatar, Typography } from "@material-tailwind/react"; +import type { ReactNode } from 'react'; // Import ReactNode for typing + +// Define the props interface based on PropTypes +interface MessageCardProps { + img: string; + name: string; + message: ReactNode; // PropTypes.node translates to ReactNode + action?: ReactNode; // Optional due to defaultProps, ReactNode can be null +} + +export function MessageCard({ img, name, message, action = null }: MessageCardProps) { + // Default value for action is handled by default parameter syntax + return ( +
    +
    + +
    + + {name} + + + {message} + +
    +
    + {action} +
    + ); +} + +// PropTypes and defaultProps are no longer needed in TypeScript when using interfaces and default parameters. +// MessageCard.defaultProps = { +// action: null, +// }; + +// MessageCard.propTypes = { +// img: PropTypes.string.isRequired, +// name: PropTypes.string.isRequired, +// message: PropTypes.node.isRequired, +// action: PropTypes.node, +// }; + +MessageCard.displayName = "/src/widgets/cards/message-card.tsx"; // Updated displayName + +export default MessageCard; diff --git a/src/widgets/cards/profile-info-card.jsx b/src/widgets/cards/profile-info-card.tsx similarity index 60% rename from src/widgets/cards/profile-info-card.jsx rename to src/widgets/cards/profile-info-card.tsx index abbba0b9..56967a21 100644 --- a/src/widgets/cards/profile-info-card.jsx +++ b/src/widgets/cards/profile-info-card.tsx @@ -1,12 +1,27 @@ -import PropTypes from "prop-types"; import { Card, CardHeader, CardBody, Typography, } from "@material-tailwind/react"; +import type { ReactNode } from 'react'; -export function ProfileInfoCard({ title, description, details, action }) { +// Define the props interface +interface ProfileInfoCardProps { + title: string; + description?: ReactNode; // Optional, defaults to null + details?: { + [key: string]: ReactNode; // Object with string keys and ReactNode values + }; // Optional, defaults to {} + action?: ReactNode; // Optional, defaults to null +} + +export function ProfileInfoCard({ + title, + description = null, + details = {}, + action = null, +}: ProfileInfoCardProps) { return ( - {description && ( + {description && ( // description can be null, so this check is fine )} - {description && details ? ( + {description && details && Object.keys(details).length > 0 ? ( // Check if details is not empty
    ) : null} - {details && ( + {details && Object.keys(details).length > 0 && ( // Check if details is not empty
      - {Object.keys(details).map((el, key) => ( + {Object.keys(details).map((el, key) => ( // el is string, key is number (index)
    • {el}: + {/* details[el] is ReactNode */} {typeof details[el] === "string" ? ( - {footer && ( + {footer && ( // footer can be null, so this check is fine {footer} @@ -36,40 +74,8 @@ export function StatisticsCard({ color, icon, title, value, footer }) { ); } -StatisticsCard.defaultProps = { - color: "blue", - footer: null, -}; - -StatisticsCard.propTypes = { - color: PropTypes.oneOf([ - "white", - "blue-gray", - "gray", - "brown", - "deep-orange", - "orange", - "amber", - "yellow", - "lime", - "light-green", - "green", - "teal", - "cyan", - "light-blue", - "blue", - "indigo", - "deep-purple", - "purple", - "pink", - "red", - ]), - icon: PropTypes.node.isRequired, - title: PropTypes.node.isRequired, - value: PropTypes.node.isRequired, - footer: PropTypes.node, -}; +// PropTypes and defaultProps are removed. -StatisticsCard.displayName = "/src/widgets/cards/statistics-card.jsx"; +StatisticsCard.displayName = "/src/widgets/cards/statistics-card.tsx"; // Updated displayName export default StatisticsCard; diff --git a/src/widgets/charts/index.js b/src/widgets/charts/index.ts similarity index 100% rename from src/widgets/charts/index.js rename to src/widgets/charts/index.ts diff --git a/src/widgets/charts/statistics-chart.jsx b/src/widgets/charts/statistics-chart.jsx deleted file mode 100644 index 015276f7..00000000 --- a/src/widgets/charts/statistics-chart.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import { - Card, - CardHeader, - CardBody, - CardFooter, - Typography, -} from "@material-tailwind/react"; -import PropTypes from "prop-types"; -import Chart from "react-apexcharts"; - -export function StatisticsChart({ color, chart, title, description, footer }) { - return ( - - - - - - - {title} - - - {description} - - - {footer && ( - - {footer} - - )} - - ); -} - -StatisticsChart.defaultProps = { - color: "blue", - footer: null, -}; - -StatisticsChart.propTypes = { - color: PropTypes.oneOf([ - "white", - "blue-gray", - "gray", - "brown", - "deep-orange", - "orange", - "amber", - "yellow", - "lime", - "light-green", - "green", - "teal", - "cyan", - "light-blue", - "blue", - "indigo", - "deep-purple", - "purple", - "pink", - "red", - ]), - chart: PropTypes.object.isRequired, - title: PropTypes.node.isRequired, - description: PropTypes.node.isRequired, - footer: PropTypes.node, -}; - -StatisticsChart.displayName = "/src/widgets/charts/statistics-chart.jsx"; - -export default StatisticsChart; diff --git a/src/widgets/charts/statistics-chart.tsx b/src/widgets/charts/statistics-chart.tsx new file mode 100644 index 00000000..847fc986 --- /dev/null +++ b/src/widgets/charts/statistics-chart.tsx @@ -0,0 +1,63 @@ +import { + Card, + CardHeader, + CardBody, + CardFooter, + Typography, +} from "@material-tailwind/react"; +import Chart from "react-apexcharts"; +import type { Props as ApexChartProps } from 'react-apexcharts'; // Import type for ApexCharts props +import type { ReactNode } from 'react'; +// Assuming MaterialColor is exported from statistics-card.tsx or defined/imported here +// For now, let's copy it here if not sure about export from other widget file. +// Ideally, it would be in a shared types file or exported from where it's defined. +export type MaterialColor = + | "white" | "blue-gray" | "gray" | "brown" | "deep-orange" + | "orange" | "amber" | "yellow" | "lime" | "light-green" + | "green" | "teal" | "cyan" | "light-blue" | "blue" + | "indigo" | "deep-purple" | "purple" | "pink" | "red"; + +// Define the props interface +interface StatisticsChartProps { + color?: MaterialColor; + chart: ApexChartProps; // Use ApexChartProps for the chart object + title: ReactNode; + description: ReactNode; + footer?: ReactNode; +} + +export function StatisticsChart({ + color = "blue", + chart, + title, + description, + footer = null, +}: StatisticsChartProps) { + return ( + + + {/* Ensure the 'chart' object passed conforms to ApexChartProps */} + + + + + {title} + + + {description} + + + {footer && ( // footer can be null + + {footer} + + )} + + ); +} + +// PropTypes and defaultProps are removed. + +StatisticsChart.displayName = "/src/widgets/charts/statistics-chart.tsx"; // Updated displayName + +export default StatisticsChart; diff --git a/src/widgets/layout/configurator.jsx b/src/widgets/layout/configurator.tsx similarity index 74% rename from src/widgets/layout/configurator.jsx rename to src/widgets/layout/configurator.tsx index 5708f333..eb3f9801 100644 --- a/src/widgets/layout/configurator.jsx +++ b/src/widgets/layout/configurator.tsx @@ -14,39 +14,63 @@ import { setSidenavType, setFixedNavbar, } from "@/context"; +import type { Dispatch } from 'react'; -function formatNumber(number, decPlaces) { +// Define types for context and state +type SidenavThemeColor = "white" | "dark" | "green" | "orange" | "red" | "pink"; +type SidenavUiType = "dark" | "white" | "transparent"; + +interface MaterialTailwindState { + openConfigurator: boolean; + sidenavColor: SidenavThemeColor; + sidenavType: SidenavUiType; + fixedNavbar: boolean; + // other properties if they exist in the actual context state +} + +type MaterialTailwindAction = + | { type: "OPEN_CONFIGURATOR"; value: boolean } + | { type: "SIDENAV_COLOR"; value: SidenavThemeColor } + | { type: "SIDENAV_TYPE"; value: SidenavUiType } + | { type: "FIXED_NAVBAR"; value: boolean } + | { type: string; value?: any }; // Fallback for other actions + +type MaterialTailwindDispatch = Dispatch; + +// Type for sidenavColors object +type SidenavColorsStyleMap = Record; + + +function formatNumber(number: number, decPlaces: number): string | number { decPlaces = Math.pow(10, decPlaces); const abbrev = ["K", "M", "B", "T"]; + let numStr: string | number = number; for (let i = abbrev.length - 1; i >= 0; i--) { var size = Math.pow(10, (i + 1) * 3); if (size <= number) { - number = Math.round((number * decPlaces) / size) / decPlaces; + numStr = Math.round((number * decPlaces) / size) / decPlaces; - if (number == 1000 && i < abbrev.length - 1) { - number = 1; + if (numStr == 1000 && i < abbrev.length - 1) { + numStr = 1; i++; } - - number += abbrev[i]; - + numStr += abbrev[i]; break; } } - - return number; + return numStr; } export function Configurator() { - const [controller, dispatch] = useMaterialTailwindController(); + const [controller, dispatch] = useMaterialTailwindController() as [MaterialTailwindState, MaterialTailwindDispatch]; const { openConfigurator, sidenavColor, sidenavType, fixedNavbar } = controller; - const [stars, setStars] = React.useState(0); + const [stars, setStars] = React.useState(0); // stars can be string like "6.8K" - const sidenavColors = { + const sidenavColors: SidenavColorsStyleMap = { white: "from-gray-100 to-gray-100 border-gray-200", dark: "from-black to-black border-gray-200", green: "from-green-400 to-green-600", @@ -56,11 +80,16 @@ export function Configurator() { }; React.useEffect(() => { - const stars = fetch( + fetch( "https://api.github.com/repos/creativetimofficial/material-tailwind-dashboard-react" ) .then((response) => response.json()) - .then((data) => setStars(formatNumber(data.stargazers_count, 1))); + .then((data) => { + if (data && typeof data.stargazers_count === 'number') { + setStars(formatNumber(data.stargazers_count, 1)); + } + }) + .catch(error => console.error("Error fetching GitHub stars:", error)); }, []); return ( @@ -92,11 +121,12 @@ export function Configurator() { Sidenav Colors
      - {Object.keys(sidenavColors).map((color) => ( + {/* Explicitly cast Object.keys to SidenavThemeColor[] */} + {(Object.keys(sidenavColors) as SidenavThemeColor[]).map((color: SidenavThemeColor) => ( @@ -141,7 +171,8 @@ export function Configurator() { setFixedNavbar(dispatch, !fixedNavbar)} />
      @@ -149,7 +180,7 @@ export function Configurator() {
      diff --git a/src/widgets/layout/footer.jsx b/src/widgets/layout/footer.jsx deleted file mode 100644 index 1ea98e53..00000000 --- a/src/widgets/layout/footer.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import PropTypes from "prop-types"; -import { Typography } from "@material-tailwind/react"; -import { HeartIcon } from "@heroicons/react/24/solid"; - -export function Footer({ brandName, brandLink, routes }) { - const year = new Date().getFullYear(); - - return ( - - ); -} - -Footer.defaultProps = { - brandName: "Creative Tim", - brandLink: "https://www.creative-tim.com", - routes: [ - { name: "Creative Tim", path: "https://www.creative-tim.com" }, - { name: "About Us", path: "https://www.creative-tim.com/presentation" }, - { name: "Blog", path: "https://www.creative-tim.com/blog" }, - { name: "License", path: "https://www.creative-tim.com/license" }, - ], -}; - -Footer.propTypes = { - brandName: PropTypes.string, - brandLink: PropTypes.string, - routes: PropTypes.arrayOf(PropTypes.object), -}; - -Footer.displayName = "/src/widgets/layout/footer.jsx"; - -export default Footer; diff --git a/src/widgets/layout/footer.tsx b/src/widgets/layout/footer.tsx new file mode 100644 index 00000000..e293e2b4 --- /dev/null +++ b/src/widgets/layout/footer.tsx @@ -0,0 +1,75 @@ +import { Typography } from "@material-tailwind/react"; +import { HeartIcon } from "@heroicons/react/24/solid"; + +// Define interfaces for props +interface FooterRoute { + name: string; + path: string; +} + +interface FooterProps { + brandName?: string; + brandLink?: string; + routes?: FooterRoute[]; +} + +// Default props values +const defaultBrandName = "Creative Tim"; +const defaultBrandLink = "https://www.creative-tim.com"; +const defaultRoutes: FooterRoute[] = [ + { name: "Creative Tim", path: "https://www.creative-tim.com" }, + { name: "About Us", path: "https://www.creative-tim.com/presentation" }, + { name: "Blog", path: "https://www.creative-tim.com/blog" }, + { name: "License", path: "https://www.creative-tim.com/license" }, +]; + +export function Footer({ + brandName = defaultBrandName, + brandLink = defaultBrandLink, + routes = defaultRoutes, +}: FooterProps) { + const year = new Date().getFullYear(); + + return ( +
      +
      + + © {year}, made with{" "} + by{" "} + + {brandName} {/* brandName is string */} + {" "} + for a better web. + +
        + {/* routes is FooterRoute[] */} + {routes.map(({ name, path }: FooterRoute) => ( +
      • + + {name} {/* name is string */} + +
      • + ))} +
      +
      +
      + ); +} + +// PropTypes and defaultProps are removed. + +Footer.displayName = "/src/widgets/layout/footer.tsx"; // Updated displayName + +export default Footer; diff --git a/src/widgets/layout/index.js b/src/widgets/layout/index.ts similarity index 100% rename from src/widgets/layout/index.js rename to src/widgets/layout/index.ts diff --git a/src/widgets/layout/navbar.jsx b/src/widgets/layout/navbar.tsx similarity index 53% rename from src/widgets/layout/navbar.jsx rename to src/widgets/layout/navbar.tsx index ba0bd4c7..26ea7892 100644 --- a/src/widgets/layout/navbar.jsx +++ b/src/widgets/layout/navbar.tsx @@ -1,5 +1,4 @@ import React from "react"; -import PropTypes from "prop-types"; import { Link } from "react-router-dom"; import { Navbar as MTNavbar, @@ -9,20 +8,52 @@ import { IconButton, } from "@material-tailwind/react"; import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"; +import type { ReactNode, ComponentType, SVGProps, ReactElement } from 'react'; -export function Navbar({ brandName, routes, action }) { +// Define types for props +type IconType = ComponentType & { className?: string; strokeWidth?: number }>; + +interface NavbarRoute { + name: string; + path: string; + icon?: IconType; +} + +interface NavbarProps { + brandName?: string; + routes: NavbarRoute[]; // routes is required + action?: ReactElement; // action is a ReactElement because React.cloneElement is used +} + +// Define default action button +const defaultAction = ( + + + +); + +export function Navbar({ + brandName = "Material Tailwind React", + routes, + action = defaultAction, +}: NavbarProps) { const [openNav, setOpenNav] = React.useState(false); React.useEffect(() => { - window.addEventListener( - "resize", - () => window.innerWidth >= 960 && setOpenNav(false) - ); + const handleResize = () => window.innerWidth >= 960 && setOpenNav(false); + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); }, []); const navList = (
        - {routes.map(({ name, path, icon }) => ( + {routes.map(({ name, path, icon: IconComponent }) => ( // Renamed icon to IconComponent for clarity - {icon && - React.createElement(icon, { + {IconComponent && // Check if IconComponent exists + React.createElement(IconComponent, { // Use IconComponent className: "w-[18px] h-[18px] opacity-50 mr-1", })} {name} @@ -54,7 +85,8 @@ export function Navbar({ brandName, routes, action }) {
        {navList}
        - {React.cloneElement(action, { + {/* Ensure action is a valid ReactElement before cloning */} + {React.isValidElement(action) && React.cloneElement(action, { className: "hidden lg:inline-block", })}
        {navList} - {React.cloneElement(action, { + {/* Ensure action is a valid ReactElement before cloning */} + {React.isValidElement(action) && React.cloneElement(action, { className: "w-full block lg:hidden", })}
        @@ -82,26 +115,8 @@ export function Navbar({ brandName, routes, action }) { ); } -Navbar.defaultProps = { - brandName: "Material Tailwind React", - action: ( - - - - ), -}; - -Navbar.propTypes = { - brandName: PropTypes.string, - routes: PropTypes.arrayOf(PropTypes.object).isRequired, - action: PropTypes.node, -}; +// PropTypes and defaultProps are removed -Navbar.displayName = "/src/widgets/layout/navbar.jsx"; +Navbar.displayName = "/src/widgets/layout/navbar.tsx"; // Updated displayName export default Navbar; diff --git a/src/widgets/layout/sidenav.jsx b/src/widgets/layout/sidenav.tsx similarity index 53% rename from src/widgets/layout/sidenav.jsx rename to src/widgets/layout/sidenav.tsx index cc7e6ffe..0fa90c4d 100644 --- a/src/widgets/layout/sidenav.jsx +++ b/src/widgets/layout/sidenav.tsx @@ -7,12 +7,63 @@ import { IconButton, Typography, } from "@material-tailwind/react"; +import { Link, NavLink } from "react-router-dom"; +import { XMarkIcon } from "@heroicons/react/24/outline"; +import { + Button, + IconButton, + Typography, +} from "@material-tailwind/react"; // Avatar removed as brandImg is not used for an Avatar here import { useMaterialTailwindController, setOpenSidenav } from "@/context"; +import type { ReactNode, Dispatch } from 'react'; + +// Define types for props and context +interface SidenavPage { + icon: ReactNode; + name: string; + path: string; +} + +interface SidenavRouteGroup { + layout: string; + title?: string; + pages: SidenavPage[]; +} -export function Sidenav({ brandImg, brandName, routes }) { - const [controller, dispatch] = useMaterialTailwindController(); +interface SidenavProps { + brandImg?: string; // Kept for API consistency, though not used in this component's JSX + brandName?: string; + routes: SidenavRouteGroup[]; +} + +// Types for MaterialTailwindController context +type SidenavType = "dark" | "white" | "transparent"; // More specific + +interface MaterialTailwindState { + sidenavColor: string; // Or MaterialColor if applicable + sidenavType: SidenavType; + openSidenav: boolean; + // other state properties... +} + +type MaterialTailwindAction = + | { type: "OPEN_SIDENAV"; value: boolean } + | { type: string; value?: any }; + +type MaterialTailwindDispatch = Dispatch; + +const defaultBrandName = "Material Tailwind React"; +// const defaultBrandImg = "/img/logo-ct.png"; // Not directly used in this component's render + +export function Sidenav({ + // brandImg = defaultBrandImg, // Default parameter for unused prop + brandName = defaultBrandName, + routes, +}: SidenavProps) { + const [controller, dispatch] = useMaterialTailwindController() as [MaterialTailwindState, MaterialTailwindDispatch]; const { sidenavColor, sidenavType, openSidenav } = controller; - const sidenavTypes = { + + const sidenavTypes: Record = { // Typed the sidenavTypes object dark: "bg-gradient-to-br from-gray-800 to-gray-900", white: "bg-white shadow-sm", transparent: "bg-transparent", @@ -20,13 +71,11 @@ export function Sidenav({ brandImg, brandName, routes }) { return (