create-bdpa-react-scaffold 1.8.8 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
package/create-ui-lib.js CHANGED
@@ -354,9 +354,15 @@ module.exports = {
354
354
  write("vite.config.mts", `
355
355
  import { defineConfig } from "vite";
356
356
  import react from "@vitejs/plugin-react-swc";
357
+ import path from "path";
357
358
 
358
359
  export default defineConfig({
359
360
  plugins: [react()],
361
+ resolve: {
362
+ alias: {
363
+ "@": path.resolve(__dirname, "./src"),
364
+ },
365
+ },
360
366
  server: {
361
367
  host: true,
362
368
  port: 3000
@@ -369,6 +375,7 @@ write("index.html", `
369
375
  <html lang="en">
370
376
  <head>
371
377
  <meta charset="UTF-8" />
378
+ <link rel="icon" type="image/png" href="/BDPA_edited.png" />
372
379
  <title>BDPA React Scaffold and Demo</title>
373
380
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
374
381
  </head>
@@ -390,7 +397,7 @@ write("HOWTO.md", `
390
397
  // src/pages/Dashboard.jsx
391
398
  import React from "react";
392
399
  import Container from "../components/layout/Container";
393
- import Card from "../components/ui/Card";
400
+ import { Card, CardContent } from "@/components/ui/card";
394
401
 
395
402
  export default function Dashboard() {
396
403
  return (
@@ -622,14 +629,13 @@ import ReactDOM from "react-dom/client";
622
629
  import { BrowserRouter } from "react-router-dom";
623
630
  import App from "./App.jsx";
624
631
  import "./index.css";
625
- import { ToastProvider } from "./components/ui/ToastProvider.jsx";
632
+ import { Toaster } from "@/components/ui/sonner";
626
633
 
627
634
  ReactDOM.createRoot(document.getElementById("root")).render(
628
635
  <React.StrictMode>
629
636
  <BrowserRouter>
630
- <ToastProvider>
631
- <App />
632
- </ToastProvider>
637
+ <App />
638
+ <Toaster />
633
639
  </BrowserRouter>
634
640
  </React.StrictMode>
635
641
  );
@@ -651,15 +657,6 @@ export function cn(...inputs) {
651
657
 
652
658
  write("src/index.js", `
653
659
  export { Button } from "./components/ui/button.jsx";
654
- export { default as Card } from "./components/ui/Card.jsx";
655
- export { default as Input } from "./components/ui/Input.jsx";
656
- export { default as FormField } from "./components/ui/FormField.jsx";
657
- export { default as Table } from "./components/ui/Table.jsx";
658
- export { default as Navbar } from "./components/ui/Navbar.jsx";
659
- export { default as Sidebar } from "./components/ui/Sidebar.jsx";
660
- export { default as Modal } from "./components/ui/Modal.jsx";
661
- export { default as Tabs } from "./components/ui/Tabs.jsx";
662
- export { ToastProvider, useToast } from "./components/ui/ToastProvider.jsx";
663
660
 
664
661
  export { default as Login } from "./pages/auth/Login.jsx";
665
662
  export { default as Register } from "./pages/auth/Register.jsx";
@@ -674,41 +671,31 @@ export { hashPassword, verifyPassword, getPasswordStrength, getPasswordStrengthL
674
671
  write("src/App.jsx", `
675
672
  import { useState } from "react";
676
673
  import { Routes, Route, useNavigate } from "react-router-dom";
677
- import {
678
- Button,
679
- Card,
680
- Input,
681
- FormField,
682
- Table,
683
- Navbar,
684
- Sidebar,
685
- Modal,
686
- Tabs,
687
- ApiClient,
688
- useToast,
689
- Login,
690
- Register
691
- } from "./index.js";
692
-
693
- const columns = [
694
- { key: "name", label: "Student" },
695
- { key: "course", label: "Course" },
696
- { key: "status", label: "Status" }
697
- ];
698
-
699
- const data = [
674
+ import { Button } from "@/components/ui/button";
675
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
676
+ import { Input } from "@/components/ui/input";
677
+ import { Label } from "@/components/ui/label";
678
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
679
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
680
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
681
+ import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
682
+ import { toast } from "sonner";
683
+ import { Menu } from "lucide-react";
684
+ import { ApiClient } from "./utils/api.js";
685
+ import Login from "./pages/auth/Login.jsx";
686
+ import Register from "./pages/auth/Register.jsx";
687
+
688
+ const enrollmentData = [
700
689
  { name: "Alex", course: "Web Design Fundamentals", status: "Enrolled" },
701
690
  { name: "Jordan", course: "Advanced Web App Design", status: "Waitlisted" },
702
691
  { name: "Taylor", course: "eSports Strategy", status: "Enrolled" }
703
692
  ];
704
693
 
705
694
  function Dashboard() {
706
- const [sidebarOpen, setSidebarOpen] = useState(false);
707
695
  const [modalOpen, setModalOpen] = useState(false);
708
696
  const [posts, setPosts] = useState([]);
709
697
  const [loadingPosts, setLoadingPosts] = useState(false);
710
698
  const [postsError, setPostsError] = useState("");
711
- const toast = useToast();
712
699
  const navigate = useNavigate();
713
700
  const client = new ApiClient("https://jsonplaceholder.typicode.com");
714
701
 
@@ -724,129 +711,183 @@ function Dashboard() {
724
711
  setLoadingPosts(false);
725
712
  };
726
713
 
727
- const tabs = [
728
- { label: "Overview", content: <p>Welcome to the BDPA React Scaffold and Demo.</p> },
729
- { label: "Components", content: <p>Buttons, Cards, Inputs, Tables, and more.</p> },
730
- { label: "Auth", content: <p>Login + Registration pages included.</p> }
714
+ const navLinks = [
715
+ { label: "Home", href: "/" },
716
+ { label: "Login", href: "/login" },
717
+ { label: "Register", href: "/register" }
731
718
  ];
732
719
 
733
720
  return (
734
721
  <div className="flex h-screen overflow-hidden">
735
722
 
736
- {/* Sidebar */}
737
- <Sidebar
738
- open={sidebarOpen}
739
- onToggle={() => setSidebarOpen(!sidebarOpen)}
740
- links={[
741
- { label: "Home", href: "/" },
742
- { label: "Login", href: "/login" },
743
- { label: "Register", href: "/register" }
744
- ]}
745
- />
723
+ {/* Sidebar (desktop) */}
724
+ <aside className="hidden md:flex flex-col w-64 border-r bg-white">
725
+ <div className="p-4 border-b font-semibold">Menu</div>
726
+ <nav className="p-4 space-y-1">
727
+ {navLinks.map((l) => (
728
+ <a key={l.label} href={l.href} className="block px-2 py-2 rounded hover:bg-gray-100 text-sm">
729
+ {l.label}
730
+ </a>
731
+ ))}
732
+ </nav>
733
+ </aside>
746
734
 
747
735
  {/* Main content */}
748
736
  <div className="flex-1 flex flex-col">
749
737
 
750
738
  {/* Navbar */}
751
- <Navbar onMenuClick={() => setSidebarOpen(!sidebarOpen)} />
739
+ <nav className="bg-white border-b px-4 py-3 flex items-center justify-between">
740
+ {/* Mobile sidebar trigger */}
741
+ <Sheet>
742
+ <SheetTrigger asChild>
743
+ <Button variant="ghost" size="icon" className="md:hidden">
744
+ <Menu />
745
+ </Button>
746
+ </SheetTrigger>
747
+ <SheetContent side="left" className="w-64 p-0">
748
+ <div className="p-4 border-b font-semibold">Menu</div>
749
+ <nav className="p-4 space-y-1">
750
+ {navLinks.map((l) => (
751
+ <a key={l.label} href={l.href} className="block px-2 py-2 rounded hover:bg-gray-100 text-sm">
752
+ {l.label}
753
+ </a>
754
+ ))}
755
+ </nav>
756
+ </SheetContent>
757
+ </Sheet>
758
+ <h1 className="text-xl font-bold">BDPA React Scaffold and Demo</h1>
759
+ <div />
760
+ </nav>
752
761
 
753
762
  {/* Page content */}
754
763
  <div className="p-6 space-y-6 overflow-auto">
755
764
 
756
765
  {/* BDPA Logo Section */}
757
766
  <Card className="text-center">
758
- <img
759
- src="/src/assets/images/BDPA_edited.avif"
760
- alt="BDPA Logo"
761
- className="h-32 mx-auto mb-4"
762
- />
763
- <h1 className="text-3xl font-bold text-blue-600">Welcome to BDPA</h1>
764
- <p className="text-gray-600 mt-2">Black Data Professionals Association</p>
767
+ <CardContent className="pt-6">
768
+ <img
769
+ src="/BDPA_edited.png"
770
+ alt="BDPA Logo"
771
+ className="h-32 mx-auto mb-4"
772
+ />
773
+ <h1 className="text-3xl font-bold text-blue-600">Welcome to BDPA</h1>
774
+ <p className="text-gray-600 mt-2">Black Data Professionals Association</p>
775
+ </CardContent>
765
776
  </Card>
766
777
 
767
- <Tabs tabs={tabs} />
778
+ {/* Tabs */}
779
+ <Tabs defaultValue="overview">
780
+ <TabsList>
781
+ <TabsTrigger value="overview">Overview</TabsTrigger>
782
+ <TabsTrigger value="components">Components</TabsTrigger>
783
+ <TabsTrigger value="auth">Auth</TabsTrigger>
784
+ </TabsList>
785
+ <TabsContent value="overview"><p className="mt-2 text-sm">Welcome to the BDPA React Scaffold and Demo.</p></TabsContent>
786
+ <TabsContent value="components"><p className="mt-2 text-sm">Buttons, Cards, Inputs, Tables, and more.</p></TabsContent>
787
+ <TabsContent value="auth"><p className="mt-2 text-sm">Login + Registration pages included.</p></TabsContent>
788
+ </Tabs>
768
789
 
769
790
  <div className="grid md:grid-cols-2 gap-6">
770
791
 
771
792
  {/* Form/Card example */}
772
793
  <Card>
773
- <h2 className="text-lg font-semibold mb-4">Sample Form</h2>
774
-
775
- <div className="space-y-4">
776
- <FormField label="Student Name">
777
- <Input placeholder="e.g. Alex Johnson" />
778
- </FormField>
779
-
780
- <FormField label="Email">
781
- <Input type="email" placeholder="student@example.com" />
782
- </FormField>
783
-
784
- <FormField label="Course">
785
- <Input placeholder="Web Design Fundamentals" />
786
- </FormField>
787
-
794
+ <CardHeader><CardTitle>Sample Form</CardTitle></CardHeader>
795
+ <CardContent className="space-y-4">
796
+ <div className="space-y-1">
797
+ <Label htmlFor="s-name">Student Name</Label>
798
+ <Input id="s-name" placeholder="e.g. Alex Johnson" />
799
+ </div>
800
+ <div className="space-y-1">
801
+ <Label htmlFor="s-email">Email</Label>
802
+ <Input id="s-email" type="email" placeholder="student@example.com" />
803
+ </div>
804
+ <div className="space-y-1">
805
+ <Label htmlFor="s-course">Course</Label>
806
+ <Input id="s-course" placeholder="Web Design Fundamentals" />
807
+ </div>
788
808
  <div className="flex gap-2 pt-2">
789
809
  <Button>Save</Button>
790
810
  <Button variant="secondary">Cancel</Button>
791
811
  </div>
792
- </div>
812
+ </CardContent>
793
813
  </Card>
794
814
 
795
815
  {/* Table example */}
796
816
  <Card>
797
- <h2 className="text-lg font-semibold mb-4">Enrollment Overview</h2>
798
- <Table columns={columns} data={data} />
817
+ <CardHeader><CardTitle>Enrollment Overview</CardTitle></CardHeader>
818
+ <CardContent>
819
+ <Table>
820
+ <TableHeader>
821
+ <TableRow>
822
+ <TableHead>Student</TableHead>
823
+ <TableHead>Course</TableHead>
824
+ <TableHead>Status</TableHead>
825
+ </TableRow>
826
+ </TableHeader>
827
+ <TableBody>
828
+ {enrollmentData.map((row) => (
829
+ <TableRow key={row.name}>
830
+ <TableCell>{row.name}</TableCell>
831
+ <TableCell>{row.course}</TableCell>
832
+ <TableCell>{row.status}</TableCell>
833
+ </TableRow>
834
+ ))}
835
+ </TableBody>
836
+ </Table>
837
+ </CardContent>
799
838
  </Card>
800
839
  </div>
801
840
 
802
- {/* Buttons */}
841
+ {/* Button Variants */}
803
842
  <Card>
804
- <h2 className="text-lg font-semibold mb-4">Button Variants</h2>
805
- <div className="flex flex-wrap gap-3">
843
+ <CardHeader><CardTitle>Button Variants</CardTitle></CardHeader>
844
+ <CardContent className="flex flex-wrap gap-3">
806
845
  <Button>Primary</Button>
807
846
  <Button variant="secondary">Secondary</Button>
808
847
  <Button variant="destructive">Danger</Button>
809
848
  <Button variant="outline">Outline</Button>
810
- </div>
849
+ </CardContent>
811
850
  </Card>
812
851
 
813
852
  {/* Live API Demo */}
814
853
  <Card>
815
- <h2 className="text-lg font-semibold mb-4">Live API Demo (JSONPlaceholder)</h2>
816
- <div className="flex items-center gap-3 mb-3">
817
- <Button onClick={fetchPosts} disabled={loadingPosts}>
818
- {loadingPosts ? "Loading..." : "Fetch Posts"}
819
- </Button>
820
- {postsError && (
821
- <span className="text-sm text-red-600">{postsError}</span>
854
+ <CardHeader><CardTitle>Live API Demo (JSONPlaceholder)</CardTitle></CardHeader>
855
+ <CardContent>
856
+ <div className="flex items-center gap-3 mb-3">
857
+ <Button onClick={fetchPosts} disabled={loadingPosts}>
858
+ {loadingPosts ? "Loading..." : "Fetch Posts"}
859
+ </Button>
860
+ {postsError && <span className="text-sm text-red-600">{postsError}</span>}
861
+ </div>
862
+ {posts.length > 0 && (
863
+ <ul className="list-disc pl-6 space-y-1">
864
+ {posts.map((p) => (
865
+ <li key={p.id} className="text-sm">
866
+ <span className="font-medium">#{p.id}</span> {p.title}
867
+ </li>
868
+ ))}
869
+ </ul>
822
870
  )}
823
- </div>
824
- {posts.length > 0 && (
825
- <ul className="list-disc pl-6 space-y-1">
826
- {posts.map((p) => (
827
- <li key={p.id} className="text-sm">
828
- <span className="font-medium">#{p.id}</span> {p.title}
829
- </li>
830
- ))}
831
- </ul>
832
- )}
871
+ </CardContent>
833
872
  </Card>
834
873
 
835
- {/* Modal + Toast */}
874
+ {/* Dialog + Toast */}
836
875
  <div className="flex gap-4">
837
- <Button onClick={() => setModalOpen(true)}>Open Modal</Button>
838
- <Button onClick={() => toast.show("This is a toast!", "success")}>
876
+ <Button onClick={() => setModalOpen(true)}>Open Dialog</Button>
877
+ <Button onClick={() => toast.success("This is a toast!")}>
839
878
  Show Toast
840
879
  </Button>
841
880
  </div>
842
881
 
843
- <Modal open={modalOpen} onClose={() => setModalOpen(false)}>
844
- <h2 className="text-lg font-semibold mb-4">Modal Title</h2>
845
- <p>This is a modal example.</p>
846
- <Button className="mt-4" onClick={() => setModalOpen(false)}>
847
- Close
848
- </Button>
849
- </Modal>
882
+ <Dialog open={modalOpen} onOpenChange={setModalOpen}>
883
+ <DialogContent>
884
+ <DialogHeader>
885
+ <DialogTitle>Dialog Title</DialogTitle>
886
+ </DialogHeader>
887
+ <p className="text-sm">This is a dialog example using shadcn/ui.</p>
888
+ <Button className="mt-4" onClick={() => setModalOpen(false)}>Close</Button>
889
+ </DialogContent>
890
+ </Dialog>
850
891
 
851
892
  </div>
852
893
  </div>
@@ -887,287 +928,9 @@ export default function App() {
887
928
  }
888
929
  `);
889
930
 
890
- // -------------------------------
891
- // UI Components
892
- // -------------------------------
893
-
894
- // Note: Button component is provided by shadcn/ui (src/components/ui/button.jsx)
895
- // Installed via: npx shadcn@latest add button
896
-
897
- write("src/components/ui/Card.jsx", `
898
- export default function Card({ children, className = "" }) {
899
- return (
900
- <div
901
- className={\`bg-white shadow-sm rounded-lg p-4 md:p-6 border border-gray-200 \${className}\`}
902
- >
903
- {children}
904
- </div>
905
- );
906
- }
907
- `);
908
-
909
- write("src/components/ui/Input.jsx", `
910
- export default function Input({ label, className = "", ...props }) {
911
- return (
912
- <label className="flex flex-col gap-1 text-sm">
913
- {label && (
914
- <span className="font-medium text-gray-700">
915
- {label}
916
- </span>
917
- )}
918
- <input
919
- className={\`border border-gray-300 rounded-md px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none text-sm \${className}\`}
920
- {...props}
921
- />
922
- </label>
923
- );
924
- }
925
- `);
926
-
927
- write("src/components/ui/FormField.jsx", `
928
- export default function FormField({ label, error, children, helperText }) {
929
- return (
930
- <div className="flex flex-col gap-1 text-sm">
931
- {label && (
932
- <label className="font-medium text-gray-700">
933
- {label}
934
- </label>
935
- )}
936
-
937
- {children}
938
-
939
- {helperText && !error && (
940
- <p className="text-xs text-gray-500">{helperText}</p>
941
- )}
942
-
943
- {error && (
944
- <p className="text-xs text-red-600">
945
- {error}
946
- </p>
947
- )}
948
- </div>
949
- );
950
- }
951
- `);
952
-
953
- write("src/components/ui/Table.jsx", `
954
- export default function Table({ columns, data }) {
955
- return (
956
- <div className="overflow-x-auto">
957
- <table className="min-w-full border border-gray-200 bg-white rounded-lg overflow-hidden">
958
- <thead className="bg-gray-100">
959
- <tr>
960
- {columns.map((col) => (
961
- <th
962
- key={col.key}
963
- className="px-4 py-2 text-left text-xs font-semibold text-gray-700 border-b border-gray-200"
964
- >
965
- {col.label}
966
- </th>
967
- ))}
968
- </tr>
969
- </thead>
970
-
971
- <tbody>
972
- {data.map((row, i) => (
973
- <tr
974
- key={i}
975
- className={i % 2 === 0 ? "bg-white" : "bg-gray-50"}
976
- >
977
- {columns.map((col) => (
978
- <td
979
- key={col.key}
980
- className="px-4 py-2 text-sm text-gray-800 border-b border-gray-100"
981
- >
982
- {row[col.key]}
983
- </td>
984
- ))}
985
- </tr>
986
- ))}
987
- </tbody>
988
- </table>
989
- </div>
990
- );
991
- }
992
- `);
993
-
994
- write("src/components/ui/Navbar.jsx", `
995
- import { Menu, ChevronDown } from "lucide-react";
996
- import { useState } from "react";
997
-
998
- export default function Navbar({ onMenuClick }) {
999
- const [dropdownOpen, setDropdownOpen] = useState(false);
1000
-
1001
- const dropdownItems = [
1002
- { label: "Profile", href: "#" },
1003
- { label: "Settings", href: "#" },
1004
- { label: "Help", href: "#" },
1005
- { label: "Feedback", href: "#" },
1006
- { label: "Logout", href: "#" }
1007
- ];
1008
-
1009
- return (
1010
- <nav className="bg-white border-b border-gray-200 px-4 py-3 flex items-center justify-between">
1011
- <button className="md:hidden" onClick={onMenuClick}>
1012
- <Menu />
1013
- </button>
1014
- <h1 className="text-xl font-bold">BDPA React Scaffold and Demo</h1>
1015
-
1016
- {/* Dropdown Menu */}
1017
- <div className="relative">
1018
- <button
1019
- onClick={() => setDropdownOpen(!dropdownOpen)}
1020
- className="flex items-center gap-2 px-3 py-2 rounded hover:bg-gray-100"
1021
- >
1022
- Menu
1023
- <ChevronDown size={18} />
1024
- </button>
1025
-
1026
- {dropdownOpen && (
1027
- <div className="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-lg shadow-lg z-50">
1028
- {dropdownItems.map((item, index) => (
1029
- <a
1030
- key={index}
1031
- href={item.href}
1032
- className="block px-4 py-2 hover:bg-gray-100 first:rounded-t-lg last:rounded-b-lg"
1033
- >
1034
- {item.label}
1035
- </a>
1036
- ))}
1037
- </div>
1038
- )}
1039
- </div>
1040
- </nav>
1041
- );
1042
- }
1043
- `);
1044
-
1045
- write("src/components/ui/Sidebar.jsx", `
1046
- import { Link } from "react-router-dom";
1047
-
1048
- export default function Sidebar({ open, onToggle, links }) {
1049
- return (
1050
- <div
1051
- className={\`
1052
- fixed md:static inset-y-0 left-0 z-40
1053
- bg-white border-r border-gray-200
1054
- h-full w-64 transform
1055
- transition-transform duration-200
1056
- \${open ? "translate-x-0" : "-translate-x-full md:translate-x-0"}
1057
- \`}
1058
- >
1059
- <div className="p-4 border-b border-gray-200 flex justify-between items-center">
1060
- <h2 className="font-semibold">Menu</h2>
1061
- <button className="md:hidden" onClick={onToggle}>✕</button>
1062
- </div>
1063
-
1064
- <ul className="p-4 space-y-2">
1065
- {links.map((l) => (
1066
- <li key={l.label}>
1067
- <Link to={l.href} className="block px-2 py-2 rounded hover:bg-gray-100">
1068
- {l.label}
1069
- </Link>
1070
- </li>
1071
- ))}
1072
- </ul>
1073
- </div>
1074
- );
1075
- }
1076
- `);
1077
-
1078
- write("src/components/ui/Modal.jsx", `
1079
- export default function Modal({ open, onClose, children }) {
1080
- if (!open) return null;
1081
-
1082
- return (
1083
- <div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
1084
- <div className="bg-white rounded-lg shadow-lg p-6 w-full max-w-md relative">
1085
- <button
1086
- className="absolute top-3 right-3 text-gray-500 hover:text-gray-700"
1087
- onClick={onClose}
1088
- >
1089
-
1090
- </button>
1091
-
1092
- {children}
1093
- </div>
1094
- </div>
1095
- );
1096
- }
1097
- `);
1098
-
1099
- write("src/components/ui/Tabs.jsx", `
1100
- import { useState } from "react";
1101
-
1102
- export default function Tabs({ tabs }) {
1103
- const [active, setActive] = useState(0);
1104
-
1105
- return (
1106
- <div>
1107
- <div className="flex gap-4 border-b border-gray-200">
1108
- {tabs.map((t, i) => (
1109
- <button
1110
- key={i}
1111
- onClick={() => setActive(i)}
1112
- className={\`pb-2 text-sm font-medium \${active === i
1113
- ? "border-b-2 border-blue-600 text-blue-600"
1114
- : "text-gray-600 hover:text-gray-800"
1115
- }\`}
1116
- >
1117
- {t.label}
1118
- </button>
1119
- ))}
1120
- </div>
1121
-
1122
- <div className="mt-4">{tabs[active].content}</div>
1123
- </div>
1124
- );
1125
- }
1126
- `);
1127
-
1128
- write("src/components/ui/ToastProvider.jsx", `
1129
- import { createContext, useContext, useState } from "react";
1130
-
1131
- const ToastContext = createContext();
1132
-
1133
- export function useToast() {
1134
- return useContext(ToastContext);
1135
- }
1136
-
1137
- export function ToastProvider({ children }) {
1138
- const [toasts, setToasts] = useState([]);
1139
-
1140
- const show = (message, type = "info") => {
1141
- const id = Date.now();
1142
- setToasts((t) => [...t, { id, message, type }]);
1143
- setTimeout(() => {
1144
- setToasts((t) => t.filter((toast) => toast.id !== id));
1145
- }, 3000);
1146
- };
1147
-
1148
- return (
1149
- <ToastContext.Provider value={{ show }}>
1150
- {children}
1151
-
1152
- <div className="fixed bottom-4 right-4 space-y-3 z-50">
1153
- {toasts.map((t) => (
1154
- <div
1155
- key={t.id}
1156
- className={\`px-4 py-2 rounded-md shadow text-white \${t.type === "success"
1157
- ? "bg-green-600"
1158
- : t.type === "error"
1159
- ? "bg-red-600"
1160
- : "bg-gray-800"
1161
- }\`}
1162
- >
1163
- {t.message}
1164
- </div>
1165
- ))}
1166
- </div>
1167
- </ToastContext.Provider>
1168
- );
1169
- }
1170
- `);
931
+ // Note: All UI components (Card, Input, Form, Table, Navbar, Tabs, Toast, etc.)
932
+ // are provided by shadcn/ui — installed via installShadcn() below.
933
+ // No manual component files are written here.
1171
934
 
1172
935
  // -------------------------------
1173
936
  // Auth Components
@@ -1175,28 +938,36 @@ export function ToastProvider({ children }) {
1175
938
 
1176
939
  write("src/pages/auth/Login.jsx", `
1177
940
  import { Button } from "@/components/ui/button";
1178
- import Input from "../../components/ui/Input.jsx";
1179
- import Card from "../../components/ui/Card.jsx";
941
+ import { Input } from "@/components/ui/input";
942
+ import { Label } from "@/components/ui/label";
943
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
1180
944
 
1181
945
  export default function Login({ onSubmit }) {
1182
946
  return (
1183
947
  <div className="flex items-center justify-center min-h-screen bg-gray-100">
1184
- <Card className="w-full max-w-sm space-y-4">
1185
- <h2 className="text-xl font-bold">Sign In</h2>
1186
-
1187
- <Input label="Email" type="email" placeholder="you@example.com" />
1188
- <Input label="Password" type="password" placeholder="••••••••" />
1189
-
1190
- <Button className="w-full" onClick={onSubmit}>
1191
- Sign In
1192
- </Button>
1193
-
1194
- <p className="text-sm text-center text-gray-600">
1195
- Don’t have an account?{" "}
1196
- <a href="/register" className="text-blue-600 hover:underline">
1197
- Create one
1198
- </a>
1199
- </p>
948
+ <Card className="w-full max-w-sm">
949
+ <CardHeader>
950
+ <CardTitle>Sign In</CardTitle>
951
+ </CardHeader>
952
+ <CardContent className="space-y-4">
953
+ <div className="space-y-1">
954
+ <Label htmlFor="email">Email</Label>
955
+ <Input id="email" type="email" placeholder="you@example.com" />
956
+ </div>
957
+ <div className="space-y-1">
958
+ <Label htmlFor="password">Password</Label>
959
+ <Input id="password" type="password" placeholder="••••••••" />
960
+ </div>
961
+ <Button className="w-full" onClick={onSubmit}>
962
+ Sign In
963
+ </Button>
964
+ <p className="text-sm text-center text-gray-600">
965
+ Don’t have an account?{" "}
966
+ <a href="/register" className="text-blue-600 hover:underline">
967
+ Create one
968
+ </a>
969
+ </p>
970
+ </CardContent>
1200
971
  </Card>
1201
972
  </div>
1202
973
  );
@@ -1204,29 +975,40 @@ export default function Login({ onSubmit }) {
1204
975
 
1205
976
  write("src/pages/auth/Register.jsx", `
1206
977
  import { Button } from "@/components/ui/button";
1207
- import Input from "../../components/ui/Input.jsx";
1208
- import Card from "../../components/ui/Card.jsx";
978
+ import { Input } from "@/components/ui/input";
979
+ import { Label } from "@/components/ui/label";
980
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
1209
981
 
1210
982
  export default function Register({ onSubmit }) {
1211
983
  return (
1212
984
  <div className="flex items-center justify-center min-h-screen bg-gray-100">
1213
- <Card className="w-full max-w-sm space-y-4">
1214
- <h2 className="text-xl font-bold">Create Account</h2>
1215
-
1216
- <Input label="Full Name" placeholder="Your Name" />
1217
- <Input label="Email" type="email" placeholder="you@example.com" />
1218
- <Input label="Password" type="password" placeholder="" />
1219
-
1220
- <Button className="w-full" onClick={onSubmit}>
1221
- Register
1222
- </Button>
1223
-
1224
- <p className="text-sm text-center text-gray-600">
1225
- Already have an account?{" "}
1226
- <a href="/login" className="text-blue-600 hover:underline">
1227
- Sign in
1228
- </a>
1229
- </p>
985
+ <Card className="w-full max-w-sm">
986
+ <CardHeader>
987
+ <CardTitle>Create Account</CardTitle>
988
+ </CardHeader>
989
+ <CardContent className="space-y-4">
990
+ <div className="space-y-1">
991
+ <Label htmlFor="name">Full Name</Label>
992
+ <Input id="name" placeholder="Your Name" />
993
+ </div>
994
+ <div className="space-y-1">
995
+ <Label htmlFor="email">Email</Label>
996
+ <Input id="email" type="email" placeholder="you@example.com" />
997
+ </div>
998
+ <div className="space-y-1">
999
+ <Label htmlFor="password">Password</Label>
1000
+ <Input id="password" type="password" placeholder="••••••••" />
1001
+ </div>
1002
+ <Button className="w-full" onClick={onSubmit}>
1003
+ Register
1004
+ </Button>
1005
+ <p className="text-sm text-center text-gray-600">
1006
+ Already have an account?{" "}
1007
+ <a href="/login" className="text-blue-600 hover:underline">
1008
+ Sign in
1009
+ </a>
1010
+ </p>
1011
+ </CardContent>
1230
1012
  </Card>
1231
1013
  </div>
1232
1014
  );
@@ -1394,18 +1176,18 @@ console.log("\nNext steps:");
1394
1176
  console.log(" 1. npm run dev");
1395
1177
  console.log(" 3. Open http://localhost:3000 in your browser\n");
1396
1178
 
1397
- // Create assets folder structure
1398
- fs.mkdirSync(path.join(BASE_DIR, "src/assets/images"), { recursive: true });
1399
- console.log("✔ Created: src/assets/images");
1179
+ // Create public folder structure
1180
+ fs.mkdirSync(path.join(BASE_DIR, "public"), { recursive: true });
1181
+ console.log("✔ Created: public");
1400
1182
 
1401
- // Copy BDPA logo image
1402
- const bdpaImagePath = path.join(__dirname, "BDPA_edited.avif");
1403
- const bdpaDestPath = path.join(BASE_DIR, "src/assets/images/BDPA_edited.avif");
1183
+ // Copy BDPA logo image to public (used as favicon)
1184
+ const bdpaImagePath = path.join(__dirname, "BDPA_edited.png");
1185
+ const bdpaDestPath = path.join(BASE_DIR, "public/BDPA_edited.png");
1404
1186
  if (fs.existsSync(bdpaImagePath)) {
1405
1187
  fs.copyFileSync(bdpaImagePath, bdpaDestPath);
1406
- console.log("✔ Copied: src/assets/images/BDPA_edited.avif\n");
1188
+ console.log("✔ Copied: public/BDPA_edited.png\n");
1407
1189
  } else {
1408
- console.log("⚠ Warning: BDPA_edited.avif not found in package\n");
1190
+ console.log("⚠ Warning: BDPA_edited.png not found in package\n");
1409
1191
  }
1410
1192
 
1411
1193
  if (doInstall) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bdpa-react-scaffold",
3
- "version": "1.8.8",
3
+ "version": "1.9.0",
4
4
  "description": "Scaffold a React + Tailwind UI library demo via Vite.",
5
5
  "bin": {
6
6
  "create-bdpa-react-scaffold": "create-ui-lib.js"
@@ -8,7 +8,7 @@
8
8
  "files": [
9
9
  "create-ui-lib.js",
10
10
  "README.md",
11
- "BDPA_edited.avif"
11
+ "BDPA_edited.png"
12
12
  ],
13
13
  "keywords": [
14
14
  "create",
package/BDPA_edited.avif DELETED
Binary file