├── .gitignore
├── README.md
├── app
├── (route)
│ ├── details
│ │ ├── [recordId]
│ │ │ └── page.js
│ │ └── _components
│ │ │ ├── BookAppointment.jsx
│ │ │ ├── DoctorDetail.jsx
│ │ │ └── DoctorSuggestionList.jsx
│ ├── my-booking
│ │ ├── _components
│ │ │ ├── BookingList.jsx
│ │ │ └── CancelAppointment.jsx
│ │ └── page.jsx
│ └── search
│ │ ├── [cname]
│ │ └── page.js
│ │ ├── _components
│ │ └── CategoryList.jsx
│ │ └── layout.js
├── _components
│ ├── CategorySearch.jsx
│ ├── DoctorList.jsx
│ ├── Footer.jsx
│ ├── Header.jsx
│ └── Hero.jsx
├── _utils
│ └── GlobalApi.jsx
├── api
│ ├── auth
│ │ └── [kindeAuth]
│ │ │ └── route.js
│ └── sendEmail
│ │ └── route.js
├── favicon.ico
├── globals.css
├── layout.js
└── page.js
├── components.json
├── components
└── ui
│ ├── alert-dialog.jsx
│ ├── button.jsx
│ ├── calendar.jsx
│ ├── command.jsx
│ ├── dialog.jsx
│ ├── input.jsx
│ ├── popover.jsx
│ ├── sonner.jsx
│ ├── tabs.jsx
│ └── textarea.jsx
├── emails
└── index.jsx
├── jsconfig.json
├── lib
└── utils.js
├── middleware.js
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── doctors.jpg
├── facebook.png
├── linkedin.png
├── logo.svg
├── next.svg
├── twitter.png
├── vercel.svg
└── youtube.png
└── tailwind.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
5 |
6 | ## Getting Started
7 |
8 | First, run the development server:
9 |
10 | ```bash
11 | npm run dev
12 | # or
13 | yarn dev
14 | # or
15 | pnpm dev
16 | # or
17 | bun dev
18 | ```
19 |
20 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
21 |
22 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
23 |
24 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
25 |
26 | ## Learn More
27 |
28 | To learn more about Next.js, take a look at the following resources:
29 |
30 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
31 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
32 |
33 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
34 |
35 | ## Deploy on Vercel
36 |
37 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
38 |
39 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
40 |
--------------------------------------------------------------------------------
/app/(route)/details/[recordId]/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import GlobalApi from '@/app/_utils/GlobalApi'
3 | import React, { useEffect, useState } from 'react'
4 | import DoctorDetail from '../_components/DoctorDetail';
5 | import DoctorSuggestionList from '../_components/DoctorSuggestionList';
6 |
7 | function Details({params}) {
8 |
9 | const [doctor,setDoctor]=useState();
10 | useEffect(()=>{
11 | getDoctorById();
12 | },[])
13 | const getDoctorById=()=>{
14 | GlobalApi.getDoctorById(params.recordId).then(resp=>{
15 | setDoctor(resp.data.data);
16 | })
17 | }
18 | return (
19 |
20 |
Details
21 |
22 |
23 | {/* Doctor Detail */}
24 |
25 | {doctor&& }
26 |
27 |
28 | {/* Doctor Suggestion */}
29 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default Details
--------------------------------------------------------------------------------
/app/(route)/details/_components/BookAppointment.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 | import {
3 | Dialog,
4 | DialogClose,
5 | DialogContent,
6 | DialogDescription,
7 | DialogFooter,
8 | DialogHeader,
9 | DialogTitle,
10 | DialogTrigger,
11 | } from "@/components/ui/dialog"
12 | import { Button } from '@/components/ui/button'
13 | import { Calendar } from "@/components/ui/calendar"
14 | import { CalendarDays, Clock } from 'lucide-react'
15 | import { Textarea } from '@/components/ui/textarea'
16 | import { useKindeBrowserClient } from '@kinde-oss/kinde-auth-nextjs'
17 | import GlobalApi from '@/app/_utils/GlobalApi'
18 | import { toast } from 'sonner'
19 |
20 | function BookAppointment({doctor}) {
21 | const [date, setDate]=useState(new Date());
22 | const [timeSlot,setTimeSlot]=useState();
23 | const [selectedTimeSlot,setSelectedTimeSlot]=useState();
24 | const [note,setNote]=useState();
25 | const {user}=useKindeBrowserClient();
26 | useEffect(()=>{
27 | getTime();
28 | },[])
29 |
30 | const getTime = () => {
31 | const timeList = [];
32 | for (let i = 10; i <= 12; i++) {
33 | timeList.push({
34 | time: i + ':00 AM'
35 | })
36 | timeList.push({
37 | time: i + ':30 AM'
38 | })
39 | }
40 | for (let i = 1; i <= 6; i++) {
41 | timeList.push({
42 | time: i + ':00 PM'
43 | })
44 | timeList.push({
45 | time: i + ':30 PM'
46 | })
47 | }
48 |
49 | setTimeSlot(timeList)
50 | }
51 |
52 | const saveBooking=()=>{
53 | const data={
54 | data:{
55 | UserName:user.given_name+" "+user.family_name,
56 | Email:user.email,
57 | Time:selectedTimeSlot,
58 | Date:date,
59 | doctor:doctor.id,
60 | Note:note
61 | }
62 | }
63 | // console.log(data)
64 | GlobalApi.bookAppointment(data).then(resp=>{
65 | console.log(resp);
66 | if(resp)
67 | {
68 | GlobalApi.sendEmail(data).then(resp=>{
69 | console.log(resp)
70 | })
71 | toast("Booking Confirmation sent on Email")
72 | }
73 | })
74 | }
75 |
76 | const isPastDay=(day)=>{
77 | return day<=new Date();
78 | }
79 | return (
80 |
81 |
82 | Book Appointment
83 |
84 |
85 |
86 |
87 | Book Appointment
88 |
89 |
90 |
91 | {/* Calender */}
92 |
93 |
94 |
95 | Select Date
96 |
97 |
104 |
105 | {/* Time Slot */}
106 |
107 |
108 |
109 | Select Time Slot
110 |
111 |
113 | {timeSlot?.map((item,index)=>(
114 |
setSelectedTimeSlot(item.time)}
116 | className={`p-2 border cursor-pointer
117 | text-center hover:bg-primary hover:text-white
118 | rounded-full
119 | ${item.time==selectedTimeSlot&&'bg-primary text-white'}`}>{item.time}
120 | ))}
121 |
122 |
123 |
124 |
126 |
127 |
128 |
129 |
130 | <>
131 |
134 | Close
135 |
136 | saveBooking()}
138 | >
139 | Submit
140 |
141 | >
142 |
143 |
144 |
145 |
146 |
147 | )
148 | }
149 |
150 | export default BookAppointment
--------------------------------------------------------------------------------
/app/(route)/details/_components/DoctorDetail.jsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@/components/ui/button'
2 | import { GraduationCap, MapPin } from 'lucide-react'
3 | import Image from 'next/image'
4 | import React from 'react'
5 | import BookAppointment from './BookAppointment'
6 |
7 | function DoctorDetail({doctor}) {
8 | const socialMediaList=[
9 | {
10 | id:1,
11 | icon:'/youtube.png',
12 | url:''
13 | },
14 | {
15 | id:2,
16 | icon:'/linkedin.png',
17 | url:''
18 | },
19 | {
20 | id:3,
21 | icon:'/twitter.png',
22 | url:''
23 | },
24 | {
25 | id:4,
26 | icon:'/facebook.png',
27 | url:''
28 | }
29 | ]
30 | return (
31 | <>
32 |
33 | {/* Doctor Image */}
34 |
35 |
41 |
42 | {/* Doctor Info */}
43 |
44 |
{doctor.attributes?.Name}
45 |
46 |
47 | {doctor.attributes?.Year_of_Experience} of Experince
48 |
49 |
50 |
51 | {doctor.attributes.Address}
52 |
53 |
{doctor.attributes?.categories.data[0].attributes?.Name}
55 |
56 |
57 | {socialMediaList.map((item,index)=>(
58 |
62 | ))}
63 |
64 |
65 |
66 |
67 | {/* About Doctor */}
68 |
69 |
70 |
71 |
72 |
About Me
73 |
{doctor.attributes.About}
74 |
75 | >
76 | )
77 | }
78 |
79 | export default DoctorDetail
--------------------------------------------------------------------------------
/app/(route)/details/_components/DoctorSuggestionList.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import GlobalApi from '@/app/_utils/GlobalApi';
3 | import Image from 'next/image';
4 | import Link from 'next/link';
5 | import React, { useEffect, useState } from 'react'
6 |
7 | function DoctorSuggestionList() {
8 | const [doctorList,setDoctorList]=useState([]);
9 | useEffect(()=>{
10 | getDoctorList();
11 | },[])
12 | const getDoctorList=()=>{
13 | GlobalApi.getDoctorList().then(resp=>{
14 | console.log(resp.data.data);
15 | setDoctorList(resp.data.data);
16 | })
17 | }
18 | return (
19 |
20 |
Suggestions
21 |
22 | {doctorList.map((doctor,index)=>(
23 |
26 |
31 |
32 |
{doctor.attributes.categories?.data[0]?.attributes?.Name}
34 | {doctor.attributes.Name}
35 |
36 | {/* */}
37 | {doctor.attributes.Year_of_Experience}
38 |
39 |
40 | ))}
41 |
42 | )
43 | }
44 |
45 | export default DoctorSuggestionList
--------------------------------------------------------------------------------
/app/(route)/my-booking/_components/BookingList.jsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@/components/ui/button'
2 | import { Calendar, Clock, MapPin } from 'lucide-react'
3 | import moment from 'moment'
4 | import Image from 'next/image'
5 | import React from 'react'
6 | import CancelAppointment from './CancelAppointment'
7 | import GlobalApi from '@/app/_utils/GlobalApi'
8 | import { toast } from 'sonner'
9 |
10 | function BookingList({bookingList,expired,updateRecord}) {
11 |
12 | const onDeleteBooking=(item)=>{
13 | console.log(item)
14 | GlobalApi.deleteBooking(item.id).then(resp=>{
15 | console.log(resp);
16 | if(resp)
17 | {
18 | toast('Booking Delete Successfully!');
19 | updateRecord()
20 | }
21 | })
22 | }
23 | return (
24 |
25 | {bookingList.length>0?bookingList.map((item,index)=>(
26 |
27 |
33 |
34 |
{item.attributes.doctor.data.attributes.Name}
35 | {!expired&&onDeleteBooking(item)}/>}
36 |
37 |
38 | {item.attributes.doctor.data.attributes.Address}
39 | Appointment On:
40 | { moment(item.attributes.Date).format('DD-MMM-YYYY')}
41 | At Time : {item.attributes.Time}
42 |
43 |
44 | ))
45 | :
46 |
47 |
48 | }
49 |
50 | )
51 | }
52 |
53 | export default BookingList
--------------------------------------------------------------------------------
/app/(route)/my-booking/_components/CancelAppointment.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | AlertDialog,
4 | AlertDialogAction,
5 | AlertDialogCancel,
6 | AlertDialogContent,
7 | AlertDialogDescription,
8 | AlertDialogFooter,
9 | AlertDialogHeader,
10 | AlertDialogTitle,
11 | AlertDialogTrigger,
12 | } from "@/components/ui/alert-dialog"
13 | import { Button } from '@/components/ui/button'
14 |
15 | function CancelAppointment({onContinueClick}) {
16 |
17 |
18 | return (
19 |
20 |
21 | Cancel Appointment
22 |
23 |
24 |
25 | Are you absolutely sure?
26 |
27 | This action cannot be undone. This will permanently delete your appointment
28 | and remove your data from our servers.
29 |
30 |
31 |
32 | Cancel
33 | onContinueClick()}>Continue
34 |
35 |
36 |
37 |
38 | )
39 | }
40 |
41 | export default CancelAppointment
--------------------------------------------------------------------------------
/app/(route)/my-booking/page.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useEffect, useState } from 'react'
3 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
4 | import BookingList from './_components/BookingList'
5 | import GlobalApi from '@/app/_utils/GlobalApi'
6 | import { useKindeBrowserClient } from '@kinde-oss/kinde-auth-nextjs'
7 |
8 | function MyBooking() {
9 |
10 | const {user}=useKindeBrowserClient();
11 | const [bookingList,setBookingList]=useState([]);
12 | useEffect(()=>{
13 | user&&getUserBookingList();
14 | },[user])
15 | const getUserBookingList=()=>{
16 | GlobalApi.getUserBookingList(user?.email).then(resp=>{
17 | console.log(resp.data.data)
18 | setBookingList(resp.data.data);
19 | })
20 | }
21 |
22 | /**
23 | * Used to Filter User Booking
24 | * @param {} type
25 | * @returns
26 | */
27 | const filterUserBooking=(type)=>{
28 | const result=bookingList.filter(item=>
29 | type=='upcoming'? new Date(item.attributes.Date)>=new Date()
30 | :new Date(item.attributes.Date)<=new Date()
31 | )
32 | console.log(result)
33 | return result;
34 | }
35 | return (
36 |
37 |
My Booking
38 |
39 |
40 | Upcoming
41 | Expired
42 |
43 |
44 | getUserBookingList()}
47 | expired={false}
48 | />
49 |
50 |
51 | getUserBookingList()}
53 | expired={true}
54 | />
55 |
56 |
57 |
58 |
59 | )
60 | }
61 |
62 | export default MyBooking
--------------------------------------------------------------------------------
/app/(route)/search/[cname]/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import DoctorList from '@/app/_components/DoctorList';
3 | import GlobalApi from '@/app/_utils/GlobalApi'
4 | import React, { useEffect, useState } from 'react'
5 |
6 | function Search({params}) {
7 |
8 | const [doctorList,setDoctorList]=useState([]);
9 | useEffect(()=>{
10 | console.log(params.cname);
11 | getDoctors();
12 | },[])
13 |
14 | const getDoctors=()=>{
15 | GlobalApi.getDoctorByCategory(params.cname).then(resp=>{
16 | setDoctorList(resp.data.data);
17 | })
18 | }
19 | return (
20 |
21 |
24 |
25 | )
26 | }
27 |
28 | export default Search
--------------------------------------------------------------------------------
/app/(route)/search/_components/CategoryList.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import GlobalApi from '@/app/_utils/GlobalApi';
3 | import React, { useEffect, useState } from 'react'
4 | import {
5 | Command,
6 | CommandDialog,
7 | CommandEmpty,
8 | CommandGroup,
9 | CommandInput,
10 | CommandItem,
11 | CommandList,
12 | CommandSeparator,
13 | CommandShortcut,
14 | } from "@/components/ui/command"
15 | import Link from 'next/link';
16 | import Image from 'next/image';
17 | import { usePathname } from 'next/navigation';
18 | function CategoryList() {
19 | const [categoryList,setCategoryList]=useState([]);
20 | const params=usePathname();
21 | const category=params.split('/')[2];
22 | useEffect(()=>{
23 | getCategoryList();
24 |
25 | },[])
26 |
27 | const getCategoryList=()=>{
28 | GlobalApi.getCategory().then(resp=>{
29 | console.log(resp.data.data);
30 | setCategoryList(resp.data.data);
31 | })
32 | }
33 | return (
34 |
35 |
36 |
37 |
38 | No results found.
39 |
40 | {categoryList&&categoryList.map((item,index)=>(
41 |
42 |
50 |
54 | {item.attributes.Name}
55 |
56 |
57 | ))}
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | )
66 | }
67 |
68 | export default CategoryList
--------------------------------------------------------------------------------
/app/(route)/search/layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import CategoryList from './_components/CategoryList'
3 |
4 | function layout({children}) {
5 | return (
6 |
7 |
8 | {/* Category */}
9 |
10 |
11 |
12 | {children}
13 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default layout
--------------------------------------------------------------------------------
/app/_components/CategorySearch.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { Button } from '@/components/ui/button'
3 | import { Input } from '@/components/ui/input'
4 | import { Search } from 'lucide-react'
5 | import React, { useEffect, useState } from 'react'
6 | import GlobalApi from '../_utils/GlobalApi'
7 | import Image from 'next/image'
8 | import Link from 'next/link'
9 |
10 | function CategorySearch() {
11 |
12 | const [categoryList,setCategoryList]=useState([]);
13 | useEffect(()=>{
14 | getCategoryList()
15 | },[])
16 |
17 | const getCategoryList=()=>{
18 | GlobalApi.getCategory().then(resp=>{
19 | console.log(resp.data.data);
20 | setCategoryList(resp.data.data);
21 | })
22 | }
23 | return (
24 |
25 |
27 | Search Doctors
28 |
Search Your Doctor and Book Appointment in one click
29 |
30 |
31 |
32 |
33 |
34 | Search
35 |
36 |
37 | {/* Display List of Category */}
38 |
39 | {categoryList.length>0?categoryList.map((item,index)=>index<6&&(
40 |
44 |
48 | {item?.attributes?.Name}
49 |
50 | ))
51 | :
52 | [1,2,3,4,5,6].map((item,index)=>(
53 |
55 |
56 |
57 | ))
58 |
59 | }
60 |
61 |
62 | )
63 | }
64 |
65 | export default CategorySearch
--------------------------------------------------------------------------------
/app/_components/DoctorList.jsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 | import Link from 'next/link'
3 | import React from 'react'
4 |
5 | function DoctorList({doctorList,heading='Popular Doctors'}) {
6 | return (
7 |
8 |
9 | {heading}
10 |
11 |
15 | {doctorList.length>0?doctorList.map((doctor,index)=>(
16 |
20 |
26 |
27 |
{doctor.attributes?.categories.data[0].attributes?.Name}
29 | {doctor.attributes.Name}
30 | {doctor.attributes?.Year_of_Experience}
31 | {doctor.attributes?.Address}
32 |
33 | Book Now
38 |
39 |
40 |
41 | ))
42 | :
43 | // Skelton Effect
44 | [1,2,3,4,5,6].map((item,index)=>(
45 |
47 |
48 |
49 | ))
50 |
51 | }
52 |
53 |
54 | )
55 | }
56 |
57 | export default DoctorList
--------------------------------------------------------------------------------
/app/_components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 | import React from 'react'
3 |
4 | function Footer() {
5 | return (
6 |
136 | )
137 | }
138 |
139 | export default Footer
--------------------------------------------------------------------------------
/app/_components/Header.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { Button } from '@/components/ui/button'
3 | import { LoginLink, LogoutLink, useKindeBrowserClient } from '@kinde-oss/kinde-auth-nextjs'
4 | import Image from 'next/image'
5 | import Link from 'next/link'
6 | import React, { useEffect } from 'react'
7 | import {
8 | Popover,
9 | PopoverContent,
10 | PopoverTrigger,
11 | } from "@/components/ui/popover"
12 |
13 |
14 | function Header() {
15 | const Menu=[
16 | {
17 | id:1,
18 | name:'Home',
19 | path:'/'
20 | },
21 | {
22 | id:2,
23 | name:'Explore',
24 | path:'/explore'
25 | },
26 | {
27 | id:3,
28 | name:'Contact Us',
29 | path:'/'
30 | },
31 | ]
32 |
33 | const {user} = useKindeBrowserClient();
34 |
35 | useEffect(()=>{
36 | console.log(user);
37 | },[user])
38 | return (
39 |
41 |
42 |
45 |
46 | {Menu.map((item,index)=>(
47 |
48 | {item.name}
51 |
52 | ))}
53 |
54 |
55 |
56 | {user?
57 |
58 |
59 |
60 | {user?.picture?
61 | :
65 | }
69 |
70 |
71 |
72 |
73 | My Booking
75 |
77 | Logout
78 |
79 |
80 |
81 |
82 |
83 | :
84 |
Get Started
85 | }
86 |
87 |
88 | )
89 | }
90 |
91 | export default Header
--------------------------------------------------------------------------------
/app/_components/Hero.jsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@/components/ui/button'
2 | import Image from 'next/image'
3 | import React from 'react'
4 |
5 | function Hero() {
6 | return (
7 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 | Find & Book
25 | Appointment
26 | with your Fav
27 | Doctors
28 |
29 |
30 | Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aut qui hic atque tenetur quis
31 | eius quos ea neque sunt, accusantium soluta minus veniam tempora deserunt? Molestiae eius
32 | quidem quam repellat.
33 |
34 |
35 |
Explore Now
36 |
37 |
38 |
39 |
40 | )
41 | }
42 |
43 | export default Hero
--------------------------------------------------------------------------------
/app/_utils/GlobalApi.jsx:
--------------------------------------------------------------------------------
1 | const { default: axios } = require("axios");
2 |
3 |
4 | const API_KEY=process.env.NEXT_PUBLIC_STRAPI_API_KEY;
5 |
6 | const axiosClient=axios.create({
7 | baseURL:'https://appointment-booking-admin.onrender.com/api',
8 | headers:{
9 | 'Authorization':`Bearer ${API_KEY}`
10 | }
11 | })
12 |
13 | const getCategory=()=>axiosClient.get('/categories?populate=*');
14 |
15 | const getDoctorList=()=>axiosClient.get('/doctors?populate=*')
16 |
17 | const getDoctorByCategory=(category)=>axiosClient.get('/doctors?filters[categories][Name][$in]='+category+"&populate=*")
18 |
19 | const getDoctorById=(id)=>axiosClient.get('/doctors/'+id+"?populate=*")
20 |
21 | const bookAppointment=(data)=>axiosClient.post('/appointments',data);
22 |
23 | const getUserBookingList=(userEmail)=>axiosClient.get("/appointments?[filters][Email][$eq]="+userEmail+"&populate[doctor][populate][image][populate][0]=url&populate=*")
24 |
25 | const deleteBooking=(id)=>axiosClient.delete('/appointments/'+id)
26 |
27 | const sendEmail=(data)=>axios.post('/api/sendEmail',data);
28 | export default{
29 | getCategory,
30 | getDoctorList,
31 | getDoctorByCategory,
32 | getDoctorById,
33 | bookAppointment,
34 | getUserBookingList,
35 | deleteBooking,
36 | sendEmail
37 | }
--------------------------------------------------------------------------------
/app/api/auth/[kindeAuth]/route.js:
--------------------------------------------------------------------------------
1 | import {handleAuth} from "@kinde-oss/kinde-auth-nextjs/server";
2 |
3 | export const GET = handleAuth();
--------------------------------------------------------------------------------
/app/api/sendEmail/route.js:
--------------------------------------------------------------------------------
1 | import { Resend } from 'resend';
2 | import { NextResponse } from 'next/server';
3 | import EmailTemplate from '@/emails';
4 |
5 | const resend = new Resend(process.env.RESEND_API_KEY);
6 |
7 | export async function POST(req){
8 |
9 | const response=await req.json()
10 | const result=response.data;
11 | try{
12 |
13 | const data=await resend.emails.send({
14 | from: 'Doctor-Appointment-Booking@tubeguruji-app.tubeguruji.com',
15 | to: [response.data.Email],
16 | subject: 'Appointment Booking Confirmation',
17 | react: EmailTemplate({result})
18 | });
19 | return NextResponse.json({data})
20 | }
21 | catch(error)
22 | {
23 | return NextResponse.json({error})
24 | }
25 | }
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rrs301/Doctor-Appointment-Booking-Web-Nextjs/7980c8892b86c991ff3e005b651ac8f8380d44b1/app/favicon.ico
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 0 0% 3.9%;
9 |
10 | --card: 0 0% 100%;
11 | --card-foreground: 0 0% 3.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 0 0% 3.9%;
15 |
16 | --primary: 0 0% 9%;
17 | --primary-foreground: 0 0% 98%;
18 |
19 | --secondary: 0 0% 96.1%;
20 | --secondary-foreground: 0 0% 9%;
21 |
22 | --muted: 0 0% 96.1%;
23 | --muted-foreground: 0 0% 45.1%;
24 |
25 | --accent: 0 0% 96.1%;
26 | --accent-foreground: 0 0% 9%;
27 |
28 | --destructive: 0 84.2% 60.2%;
29 | --destructive-foreground: 0 0% 98%;
30 |
31 | --border: 0 0% 89.8%;
32 | --input: 0 0% 89.8%;
33 | --ring: 0 0% 3.9%;
34 |
35 | --radius: 0.5rem;
36 | }
37 |
38 | .dark {
39 | --background: 0 0% 3.9%;
40 | --foreground: 0 0% 98%;
41 |
42 | --card: 0 0% 3.9%;
43 | --card-foreground: 0 0% 98%;
44 |
45 | --popover: 0 0% 3.9%;
46 | --popover-foreground: 0 0% 98%;
47 |
48 | --primary: 0 0% 98%;
49 | --primary-foreground: 0 0% 9%;
50 |
51 | --secondary: 0 0% 14.9%;
52 | --secondary-foreground: 0 0% 98%;
53 |
54 | --muted: 0 0% 14.9%;
55 | --muted-foreground: 0 0% 63.9%;
56 |
57 | --accent: 0 0% 14.9%;
58 | --accent-foreground: 0 0% 98%;
59 |
60 | --destructive: 0 62.8% 30.6%;
61 | --destructive-foreground: 0 0% 98%;
62 |
63 | --border: 0 0% 14.9%;
64 | --input: 0 0% 14.9%;
65 | --ring: 0 0% 83.1%;
66 | }
67 | }
68 |
69 | @layer base {
70 | * {
71 | @apply border-border;
72 | }
73 | body {
74 | @apply bg-background text-foreground;
75 | }
76 | }
--------------------------------------------------------------------------------
/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Outfit } from "next/font/google";
2 | import "./globals.css";
3 | import Header from "./_components/Header";
4 | import Footer from "./_components/Footer";
5 | import { Toaster } from "sonner";
6 |
7 | const outfit = Outfit({ subsets: ["latin"] });
8 |
9 | export const metadata = {
10 | title: "Create Next App",
11 | description: "Generated by create next app",
12 | };
13 |
14 | export default function RootLayout({ children }) {
15 | return (
16 |
17 |
18 |
19 |
20 | {children}
21 |
22 |
23 | {/* */}
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/app/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { Button } from "@/components/ui/button";
3 | import Image from "next/image";
4 | import Hero from "./_components/Hero";
5 | import CategorySearch from "./_components/CategorySearch";
6 | import DoctorList from "./_components/DoctorList";
7 | import GlobalApi from "./_utils/GlobalApi";
8 | import { useEffect, useState } from "react";
9 |
10 | export default function Home() {
11 |
12 | const [doctorList,setDoctorList]=useState([]);
13 | useEffect(()=>{
14 | getDoctorList();
15 | },[])
16 | const getDoctorList=()=>{
17 | GlobalApi.getDoctorList().then(resp=>{
18 | console.log(resp.data.data);
19 | setDoctorList(resp.data.data);
20 | })
21 | }
22 | return (
23 |
24 | {/* Hero Section */}
25 |
26 |
27 | {/* Search bar + Categories */}
28 |
29 |
30 | {/* Popular Doctor List */}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": false,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
--------------------------------------------------------------------------------
/components/ui/alert-dialog.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { buttonVariants } from "@/components/ui/button"
8 |
9 | const AlertDialog = AlertDialogPrimitive.Root
10 |
11 | const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12 |
13 | const AlertDialogPortal = AlertDialogPrimitive.Portal
14 |
15 | const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
16 |
23 | ))
24 | AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
25 |
26 | const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (
27 |
28 |
29 |
36 |
37 | ))
38 | AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
39 |
40 | const AlertDialogHeader = ({
41 | className,
42 | ...props
43 | }) => (
44 |
47 | )
48 | AlertDialogHeader.displayName = "AlertDialogHeader"
49 |
50 | const AlertDialogFooter = ({
51 | className,
52 | ...props
53 | }) => (
54 |
57 | )
58 | AlertDialogFooter.displayName = "AlertDialogFooter"
59 |
60 | const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (
61 |
62 | ))
63 | AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
64 |
65 | const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (
66 |
70 | ))
71 | AlertDialogDescription.displayName =
72 | AlertDialogPrimitive.Description.displayName
73 |
74 | const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (
75 |
76 | ))
77 | AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
78 |
79 | const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (
80 |
84 | ))
85 | AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
86 |
87 | export {
88 | AlertDialog,
89 | AlertDialogPortal,
90 | AlertDialogOverlay,
91 | AlertDialogTrigger,
92 | AlertDialogContent,
93 | AlertDialogHeader,
94 | AlertDialogFooter,
95 | AlertDialogTitle,
96 | AlertDialogDescription,
97 | AlertDialogAction,
98 | AlertDialogCancel,
99 | }
100 |
--------------------------------------------------------------------------------
/components/ui/button.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive:
14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15 | outline:
16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17 | secondary:
18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19 | ghost: "hover:bg-accent hover:text-accent-foreground",
20 | link: "text-primary underline-offset-4 hover:underline",
21 | },
22 | size: {
23 | default: "h-10 px-4 py-2",
24 | sm: "h-9 rounded-md px-3",
25 | lg: "h-11 rounded-md px-8",
26 | icon: "h-10 w-10",
27 | },
28 | },
29 | defaultVariants: {
30 | variant: "default",
31 | size: "default",
32 | },
33 | }
34 | )
35 |
36 | const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
37 | const Comp = asChild ? Slot : "button"
38 | return (
39 | ( )
43 | );
44 | })
45 | Button.displayName = "Button"
46 |
47 | export { Button, buttonVariants }
48 |
--------------------------------------------------------------------------------
/components/ui/calendar.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import * as React from "react"
3 | import { ChevronLeft, ChevronRight } from "lucide-react"
4 | import { DayPicker } from "react-day-picker"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { buttonVariants } from "@/components/ui/button"
8 |
9 | function Calendar({
10 | className,
11 | classNames,
12 | showOutsideDays = true,
13 | ...props
14 | }) {
15 | return (
16 | ( ,
55 | IconRight: ({ ...props }) => ,
56 | }}
57 | {...props} />)
58 | );
59 | }
60 | Calendar.displayName = "Calendar"
61 |
62 | export { Calendar }
63 |
--------------------------------------------------------------------------------
/components/ui/command.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import * as React from "react"
3 | import { Command as CommandPrimitive } from "cmdk"
4 | import { Search } from "lucide-react"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { Dialog, DialogContent } from "@/components/ui/dialog"
8 |
9 | const Command = React.forwardRef(({ className, ...props }, ref) => (
10 |
17 | ))
18 | Command.displayName = CommandPrimitive.displayName
19 |
20 | const CommandDialog = ({
21 | children,
22 | ...props
23 | }) => {
24 | return (
25 | (
26 |
27 |
29 | {children}
30 |
31 |
32 | )
33 | );
34 | }
35 |
36 | const CommandInput = React.forwardRef(({ className, ...props }, ref) => (
37 |
38 |
39 |
46 |
47 | ))
48 |
49 | CommandInput.displayName = CommandPrimitive.Input.displayName
50 |
51 | const CommandList = React.forwardRef(({ className, ...props }, ref) => (
52 |
56 | ))
57 |
58 | CommandList.displayName = CommandPrimitive.List.displayName
59 |
60 | const CommandEmpty = React.forwardRef((props, ref) => (
61 |
62 | ))
63 |
64 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName
65 |
66 | const CommandGroup = React.forwardRef(({ className, ...props }, ref) => (
67 |
74 | ))
75 |
76 | CommandGroup.displayName = CommandPrimitive.Group.displayName
77 |
78 | const CommandSeparator = React.forwardRef(({ className, ...props }, ref) => (
79 |
80 | ))
81 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName
82 |
83 | const CommandItem = React.forwardRef(({ className, ...props }, ref) => (
84 |
91 | ))
92 |
93 | CommandItem.displayName = CommandPrimitive.Item.displayName
94 |
95 | const CommandShortcut = ({
96 | className,
97 | ...props
98 | }) => {
99 | return (
100 | ( )
103 | );
104 | }
105 | CommandShortcut.displayName = "CommandShortcut"
106 |
107 | export {
108 | Command,
109 | CommandDialog,
110 | CommandInput,
111 | CommandList,
112 | CommandEmpty,
113 | CommandGroup,
114 | CommandItem,
115 | CommandShortcut,
116 | CommandSeparator,
117 | }
118 |
--------------------------------------------------------------------------------
/components/ui/dialog.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as DialogPrimitive from "@radix-ui/react-dialog"
5 | import { X } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Dialog = DialogPrimitive.Root
10 |
11 | const DialogTrigger = DialogPrimitive.Trigger
12 |
13 | const DialogPortal = DialogPrimitive.Portal
14 |
15 | const DialogClose = DialogPrimitive.Close
16 |
17 | const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
18 |
25 | ))
26 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
27 |
28 | const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (
29 |
30 |
31 |
38 | {children}
39 |
41 |
42 | Close
43 |
44 |
45 |
46 | ))
47 | DialogContent.displayName = DialogPrimitive.Content.displayName
48 |
49 | const DialogHeader = ({
50 | className,
51 | ...props
52 | }) => (
53 |
56 | )
57 | DialogHeader.displayName = "DialogHeader"
58 |
59 | const DialogFooter = ({
60 | className,
61 | ...props
62 | }) => (
63 |
66 | )
67 | DialogFooter.displayName = "DialogFooter"
68 |
69 | const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (
70 |
74 | ))
75 | DialogTitle.displayName = DialogPrimitive.Title.displayName
76 |
77 | const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (
78 |
82 | ))
83 | DialogDescription.displayName = DialogPrimitive.Description.displayName
84 |
85 | export {
86 | Dialog,
87 | DialogPortal,
88 | DialogOverlay,
89 | DialogClose,
90 | DialogTrigger,
91 | DialogContent,
92 | DialogHeader,
93 | DialogFooter,
94 | DialogTitle,
95 | DialogDescription,
96 | }
97 |
--------------------------------------------------------------------------------
/components/ui/input.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Input = React.forwardRef(({ className, type, ...props }, ref) => {
6 | return (
7 | ( )
15 | );
16 | })
17 | Input.displayName = "Input"
18 |
19 | export { Input }
20 |
--------------------------------------------------------------------------------
/components/ui/popover.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as PopoverPrimitive from "@radix-ui/react-popover"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Popover = PopoverPrimitive.Root
9 |
10 | const PopoverTrigger = PopoverPrimitive.Trigger
11 |
12 | const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
13 |
14 |
23 |
24 | ))
25 | PopoverContent.displayName = PopoverPrimitive.Content.displayName
26 |
27 | export { Popover, PopoverTrigger, PopoverContent }
28 |
--------------------------------------------------------------------------------
/components/ui/sonner.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useTheme } from "next-themes"
3 | import { Toaster as Sonner } from "sonner"
4 |
5 | const Toaster = ({
6 | ...props
7 | }) => {
8 | const { theme = "system" } = useTheme()
9 |
10 | return (
11 | ( )
26 | );
27 | }
28 |
29 | export { Toaster }
30 |
--------------------------------------------------------------------------------
/components/ui/tabs.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TabsPrimitive from "@radix-ui/react-tabs"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Tabs = TabsPrimitive.Root
9 |
10 | const TabsList = React.forwardRef(({ className, ...props }, ref) => (
11 |
18 | ))
19 | TabsList.displayName = TabsPrimitive.List.displayName
20 |
21 | const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => (
22 |
29 | ))
30 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
31 |
32 | const TabsContent = React.forwardRef(({ className, ...props }, ref) => (
33 |
40 | ))
41 | TabsContent.displayName = TabsPrimitive.Content.displayName
42 |
43 | export { Tabs, TabsList, TabsTrigger, TabsContent }
44 |
--------------------------------------------------------------------------------
/components/ui/textarea.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Textarea = React.forwardRef(({ className, ...props }, ref) => {
6 | return (
7 | ()
14 | );
15 | })
16 | Textarea.displayName = "Textarea"
17 |
18 | export { Textarea }
19 |
--------------------------------------------------------------------------------
/emails/index.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Body,
3 | Button,
4 | Container,
5 | Head,
6 | Hr,
7 | Html,
8 | Img,
9 | Preview,
10 | Section,
11 | Text,
12 | } from "@react-email/components";
13 | import * as React from "react";
14 |
15 |
16 |
17 | const baseUrl = process.env.VERCEL_URL
18 | ? `https://${process.env.VERCEL_URL}`
19 | : "http://localhost:3000";
20 |
21 | export const EmailTemplate = ({
22 | UserName,
23 | Email,
24 | Time,
25 | Date,
26 | doctor,
27 | Note
28 | }) => (
29 |
30 |
31 |
32 | The sales intelligence platform that helps you uncover qualified leads.
33 |
34 |
35 |
36 |
43 | Hi {UserName},
44 |
45 | Your Appointment with Doctor has booked on {Date} at {Time}
46 |
47 | {/*
48 |
49 | Get started
50 |
51 | */}
52 |
53 | Best,
54 |
55 | Tubeguruji App
56 |
57 |
58 |
59 | Developed by TubeGuruji!
60 |
61 |
62 |
63 |
64 | );
65 |
66 |
67 |
68 | export default EmailTemplate;
69 |
70 | const main = {
71 | backgroundColor: "#ffffff",
72 | fontFamily:
73 | '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
74 | };
75 |
76 | const container = {
77 | margin: "0 auto",
78 | padding: "20px 0 48px",
79 | };
80 |
81 | const logo = {
82 | margin: "0 auto",
83 | };
84 |
85 | const paragraph = {
86 | fontSize: "16px",
87 | lineHeight: "26px",
88 | };
89 |
90 | const btnContainer = {
91 | textAlign: "center",
92 | };
93 |
94 | const button = {
95 | backgroundColor: "#5F51E8",
96 | borderRadius: "3px",
97 | color: "#fff",
98 | fontSize: "16px",
99 | textDecoration: "none",
100 | textAlign: "center",
101 | display: "block",
102 | padding: "12px",
103 | };
104 |
105 | const hr = {
106 | borderColor: "#cccccc",
107 | margin: "20px 0",
108 | };
109 |
110 | const footer = {
111 | color: "#8898aa",
112 | fontSize: "12px",
113 | };
114 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | import { clsx } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/middleware.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from 'next/server'
2 | import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
3 | // This function can be marked `async` if using `await` inside
4 | export async function middleware(request) {
5 | const { isAuthenticated } = getKindeServerSession();
6 | if (!(await isAuthenticated())) {
7 | // redirect("/api/auth/login");
8 | return NextResponse.redirect(new URL('/api/auth/login?post_login_redirect_url=/', request.url))
9 |
10 | }
11 | }
12 |
13 | // See "Matching Paths" below to learn more
14 | export const config = {
15 | matcher: ['/details/:path*'],
16 | }
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: false,
4 | images:{
5 | domains:['res.cloudinary.com','lh3.googleusercontent.com'],
6 | unoptimized:true
7 | }
8 | };
9 |
10 | export default nextConfig;
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "doctor-appointment-booking-web-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "email": "email dev"
11 | },
12 | "dependencies": {
13 | "@kinde-oss/kinde-auth-nextjs": "^2.1.11",
14 | "@radix-ui/react-alert-dialog": "^1.0.5",
15 | "@radix-ui/react-dialog": "^1.0.5",
16 | "@radix-ui/react-popover": "^1.0.7",
17 | "@radix-ui/react-slot": "^1.0.2",
18 | "@radix-ui/react-tabs": "^1.0.4",
19 | "@react-email/components": "0.0.14",
20 | "axios": "^1.6.7",
21 | "class-variance-authority": "^0.7.0",
22 | "clsx": "^2.1.0",
23 | "cmdk": "^0.2.1",
24 | "lucide-react": "^0.330.0",
25 | "moment": "^2.30.1",
26 | "next": "14.1.0",
27 | "next-themes": "^0.2.1",
28 | "react": "^18",
29 | "react-day-picker": "^8.10.0",
30 | "react-dom": "^18",
31 | "react-email": "2.0.0",
32 | "resend": "^3.2.0",
33 | "sonner": "^1.4.0",
34 | "tailwind-merge": "^2.2.1",
35 | "tailwindcss-animate": "^1.0.7"
36 | },
37 | "devDependencies": {
38 | "autoprefixer": "^10.0.1",
39 | "postcss": "^8",
40 | "tailwindcss": "^3.3.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/doctors.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rrs301/Doctor-Appointment-Booking-Web-Nextjs/7980c8892b86c991ff3e005b651ac8f8380d44b1/public/doctors.jpg
--------------------------------------------------------------------------------
/public/facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rrs301/Doctor-Appointment-Booking-Web-Nextjs/7980c8892b86c991ff3e005b651ac8f8380d44b1/public/facebook.png
--------------------------------------------------------------------------------
/public/linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rrs301/Doctor-Appointment-Booking-Web-Nextjs/7980c8892b86c991ff3e005b651ac8f8380d44b1/public/linkedin.png
--------------------------------------------------------------------------------
/public/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rrs301/Doctor-Appointment-Booking-Web-Nextjs/7980c8892b86c991ff3e005b651ac8f8380d44b1/public/twitter.png
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/youtube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rrs301/Doctor-Appointment-Booking-Web-Nextjs/7980c8892b86c991ff3e005b651ac8f8380d44b1/public/youtube.png
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | './pages/**/*.{js,jsx}',
6 | './components/**/*.{js,jsx}',
7 | './app/**/*.{js,jsx}',
8 | './src/**/*.{js,jsx}',
9 | ],
10 | prefix: "",
11 | theme: {
12 | container: {
13 | center: true,
14 | padding: "2rem",
15 | screens: {
16 | "2xl": "1400px",
17 | },
18 | },
19 | extend: {
20 | colors: {
21 | border: "hsl(var(--border))",
22 | input: "hsl(var(--input))",
23 | ring: "hsl(var(--ring))",
24 | background: "hsl(var(--background))",
25 | foreground: "hsl(var(--foreground))",
26 | primary: {
27 | DEFAULT: "#0D7DFF",
28 | foreground: "hsl(var(--primary-foreground))",
29 | },
30 | secondary: {
31 | DEFAULT: "hsl(var(--secondary))",
32 | foreground: "hsl(var(--secondary-foreground))",
33 | },
34 | destructive: {
35 | DEFAULT: "hsl(var(--destructive))",
36 | foreground: "hsl(var(--destructive-foreground))",
37 | },
38 | muted: {
39 | DEFAULT: "hsl(var(--muted))",
40 | foreground: "hsl(var(--muted-foreground))",
41 | },
42 | accent: {
43 | DEFAULT: "hsl(var(--accent))",
44 | foreground: "hsl(var(--accent-foreground))",
45 | },
46 | popover: {
47 | DEFAULT: "hsl(var(--popover))",
48 | foreground: "hsl(var(--popover-foreground))",
49 | },
50 | card: {
51 | DEFAULT: "hsl(var(--card))",
52 | foreground: "hsl(var(--card-foreground))",
53 | },
54 | },
55 | borderRadius: {
56 | lg: "var(--radius)",
57 | md: "calc(var(--radius) - 2px)",
58 | sm: "calc(var(--radius) - 4px)",
59 | },
60 | keyframes: {
61 | "accordion-down": {
62 | from: { height: "0" },
63 | to: { height: "var(--radix-accordion-content-height)" },
64 | },
65 | "accordion-up": {
66 | from: { height: "var(--radix-accordion-content-height)" },
67 | to: { height: "0" },
68 | },
69 | },
70 | animation: {
71 | "accordion-down": "accordion-down 0.2s ease-out",
72 | "accordion-up": "accordion-up 0.2s ease-out",
73 | },
74 | },
75 | },
76 | plugins: [require("tailwindcss-animate")],
77 | }
--------------------------------------------------------------------------------