Skip to content
Open

Dev #79

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
70a7336
monorepo setup
rizwanc018 Jun 17, 2026
0beaff8
Added ui package to dependency
rizwanc018 Jun 18, 2026
998adbe
modified: apps/client/src/app/page.tsx
rizwanc018 Jun 18, 2026
45bb7a6
initial ui
rizwanc018 Jun 18, 2026
2eecf3c
Home ui fixes
rizwanc018 Jun 19, 2026
b169c06
clerk sigin and signup added
rizwanc018 Jun 19, 2026
cbe77f1
middleware added
rizwanc018 Jun 20, 2026
a593db1
db and prisma setup
rizwanc018 Jun 20, 2026
586a85f
new file: packages/database/tsconfig.json
rizwanc018 Jun 20, 2026
fde9c24
owner and user role based auth and clerk webhook
rizwanc018 Jun 21, 2026
f7c5ee9
db schema created(venue,booking,review), migrated and generated
rizwanc018 Jun 22, 2026
c0062f7
prisma generate file error fix
rizwanc018 Jun 22, 2026
8c8b89a
feat: redirect owner and user to respective pages after sign-in
rizwanc018 Jun 22, 2026
3bb5836
feat: owner portal UI
rizwanc018 Jun 22, 2026
33f0a06
new file: packages/database/prisma/migrations/20260622122959_purpo…
rizwanc018 Jun 22, 2026
ccd8bdb
feat: types package connected to api, owner middleware created
rizwanc018 Jun 22, 2026
222ff5e
feat: create venue api created,
rizwanc018 Jun 23, 2026
3e4df04
Create venue form
rizwanc018 Jun 23, 2026
e4b650b
Image upload to cloudinary
rizwanc018 Jun 23, 2026
6767c9b
venue image upload moved to last stage
rizwanc018 Jun 23, 2026
038dabe
modified: packages/database/src/client.ts
rizwanc018 Jun 24, 2026
5e59713
admin ui created
rizwanc018 Jun 24, 2026
42c5b6a
feat: admin sign-in
rizwanc018 Jun 24, 2026
ff33fd7
Admin middleware created
rizwanc018 Jun 24, 2026
0b59450
feat: created actions to fetch and update status of venues
rizwanc018 Jun 24, 2026
57986aa
fix: page name collision
rizwanc018 Jun 24, 2026
6289669
Users table fixes
rizwanc018 Jun 25, 2026
03d5397
Feat : Name col added in user table
rizwanc018 Jun 25, 2026
a4a5510
renamed: apps/admin/app/actions/venues.ts -> apps/admin/app/actio…
rizwanc018 Jun 25, 2026
f110798
Active added in veune table
rizwanc018 Jun 25, 2026
2d10ddc
feat: fetch users and owners
rizwanc018 Jun 25, 2026
a03a9c2
modified: apps/admin/components/tabs/Users.tsx
rizwanc018 Jun 25, 2026
47d9006
feat: fetch venues api created and called in home page
rizwanc018 Jun 25, 2026
de0f343
Owner register linked to CTABanner
rizwanc018 Jun 25, 2026
dc013c4
venu card created
rizwanc018 Jun 26, 2026
53be478
feat: Venue page
rizwanc018 Jun 26, 2026
544bc0e
feat: booking page, booking api created and integrated
rizwanc018 Jun 26, 2026
c2167f1
Venue types moved to types package
rizwanc018 Jun 26, 2026
c1c5770
feat: Created api to get owner's venu bookings
rizwanc018 Jun 26, 2026
13fc913
User booking details api created
rizwanc018 Jun 26, 2026
11360e0
feat : Customer and Owner Booking details page and Api created and in…
rizwanc018 Jun 27, 2026
bacc411
Bookings action created, to fetch all bookings, connected to frontend
rizwanc018 Jun 28, 2026
8d9e836
modified: apps/admin/app/actions/booking.ts
rizwanc018 Jun 28, 2026
e7f2946
modified: apps/admin/app/actions/booking.ts
rizwanc018 Jun 29, 2026
35c448b
feat: User booking history
rizwanc018 Jun 29, 2026
ca1b378
feat: User booking history
rizwanc018 Jun 29, 2026
3ee3e91
feat: created api to fetch owner venues, api integrated to dispaly ow…
rizwanc018 Jun 29, 2026
f5fba9e
fix: hydration error
rizwanc018 Jun 29, 2026
702e891
feat: Api created to get venue review status, and integrated
rizwanc018 Jun 29, 2026
09a689e
feat: Api created to get venue review status, and integrated
rizwanc018 Jun 29, 2026
dd76f19
Review modal created
rizwanc018 Jun 29, 2026
7d55040
create review api created
rizwanc018 Jun 30, 2026
e7be618
renaming controller and route files, moving schemas to seperate function
rizwanc018 Jun 30, 2026
fd856c2
feat: Integrated api to write review
rizwanc018 Jun 30, 2026
62d120b
feat: Api to get reviews of venue
rizwanc018 Jun 30, 2026
c30bcba
feat: Created api to get reviews
rizwanc018 Jun 30, 2026
8f12dfd
fix: Venue and review status query in validation on write review hook
rizwanc018 Jun 30, 2026
23e51f7
feat: middleware in client, fix: auth redirect
rizwanc018 Jun 30, 2026
7c687e9
feat: Reviews api integrated, and fetched review using this api
rizwanc018 Jul 1, 2026
05be713
chore: date selector moved to diff component
rizwanc018 Jul 1, 2026
ab74d00
Shadcn calender used, and disabled today date
rizwanc018 Jul 1, 2026
9fe16c2
fix: owner venue detail type error
rizwanc018 Jul 1, 2026
cd001fe
fix: amount convert from paisa to rupees
rizwanc018 Jul 1, 2026
7a3b4c7
feat : Disable booked dates and session
rizwanc018 Jul 1, 2026
258a48f
feat: Venue edit api created
rizwanc018 Jul 2, 2026
6345c03
feat: Edit venue modal
rizwanc018 Jul 2, 2026
ef6f293
Edit Venue api integrted
rizwanc018 Jul 2, 2026
557ca68
refactor: centralize price unit conversion in the API layer, created …
rizwanc018 Jul 2, 2026
ba73043
toast added
rizwanc018 Jul 2, 2026
8d361d8
fix: Session edit, activating all session by default
rizwanc018 Jul 2, 2026
c5115a5
fix: session active/inactive
rizwanc018 Jul 2, 2026
028fd18
Kafka and email service created
rizwanc018 Jul 3, 2026
b524eb8
feat: Email notification using kafka and nodmialer for booking and ve…
rizwanc018 Jul 3, 2026
27448bb
feat: Welcome email notification added
rizwanc018 Jul 4, 2026
3b62d62
refactor: admin logo change
rizwanc018 Jul 4, 2026
dc179ac
feat: Duplicate name and session time overlap checking
rizwanc018 Jul 4, 2026
c6e6a30
feat: duplicate session name and session time overlap check on edit v…
rizwanc018 Jul 4, 2026
d6cc64b
feat: booking table
rizwanc018 Jul 4, 2026
1bbc089
feat: owner dashboard
rizwanc018 Jul 4, 2026
703238f
feat: owner dashboard fetching limit change
rizwanc018 Jul 4, 2026
a26908b
refactor: Owner dasboard apis moved to one api
rizwanc018 Jul 4, 2026
f853935
refactor
rizwanc018 Jul 4, 2026
ba48bb4
feat: admin dashboard
rizwanc018 Jul 5, 2026
44771df
feat: venue search, and search using url
rizwanc018 Jul 5, 2026
0a14fdd
feat: Owner portal tab selection moved to url
rizwanc018 Jul 5, 2026
1f708be
feat: Venue delete api created and integrated
rizwanc018 Jul 5, 2026
c646f8d
refactor: console logs removed
rizwanc018 Jul 5, 2026
2780336
env example file created
rizwanc018 Jul 5, 2026
f1f4903
feat: api error handler
rizwanc018 Jul 5, 2026
1d86c07
fix: wrap booking creation in a transaction to prevent race condition
rizwanc018 Jul 5, 2026
d18a681
fix: amount unit conversion in booking conformation mails
rizwanc018 Jul 5, 2026
b2bf053
fix: owner auth error, promote owner role during auth-redirect inste…
rizwanc018 Jul 7, 2026
3f4bfe6
admin build fix
rizwanc018 Jul 8, 2026
ec97ef7
client build fix
rizwanc018 Jul 8, 2026
a682a76
client-api build fix
rizwanc018 Jul 8, 2026
cc294b6
email-service build fix
rizwanc018 Jul 8, 2026
2a2aaa6
modified: apps/admin/package.json
rizwanc018 Jul 8, 2026
78d2719
fix: run backend apps via tsx instead of compiled dist
rizwanc018 Jul 8, 2026
f9abdfe
Merge pull request #1 from rizwanc018/build-fix-2
rizwanc018 Jul 8, 2026
cb4ae58
feat: smooth scroll
rizwanc018 Jul 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
.turbo
.env
dist
9 changes: 9 additions & 0 deletions apps/admin/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=

NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/

CLERK_WEBHOOK_SECRET=

DATABASE_URL="postgresql://<username>:<password>@<host>:<port>/bookmyvenue?schema=public"
40 changes: 40 additions & 0 deletions apps/admin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
36 changes: 36 additions & 0 deletions apps/admin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

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.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
241 changes: 241 additions & 0 deletions apps/admin/app/(admin)/bookings/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
"use client";

import { useEffect, useState } from "react";
import { Search, Download, CalendarCheck } from "lucide-react";
import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from "../../../components/ui/table";
import { Booking, fetchBookings } from "../../actions/booking";
import { BookingStatus } from "@bookmyvenue/database/enums";
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "../../../components/ui/pagination";
import { BOOKING_STATUS_STYLE, fmtAmount } from "../../../lib/utils";
import { Skeleton } from "../../../components/ui/skeleton";

const PAGE_SIZE = 10;

export default function BookingsPage() {
const [bookingSearch, setBookingSearch] = useState("");
const [bookingFilter, setBookingFilter] = useState<BookingStatus | "All">("All");
const [total, setTotal] = useState(0);
const [page, setPage] = useState(1);
const [bookings, setBookings] = useState<Booking[]>([]);
const [loading, setLoading] = useState<boolean>(true);

const bookingFilters: (BookingStatus | "All")[] = [
"All",
...Object.values(BookingStatus).filter((status) => status !== BookingStatus.PENDING),
];

useEffect(() => {
fetchBookings("All", 1, PAGE_SIZE).then((result) => {
setTotal(result.total);
setBookings(result.bookings);
setLoading(false);
});
}, []);

const handleFilterChange = async (filter: BookingStatus | "All") => {
setBookingFilter(filter);
setPage(1);
setLoading(true);
fetchBookings(filter, 1, PAGE_SIZE).then((result) => {
setTotal(result.total);
setBookings(result.bookings);
setLoading(false);
});
};

const handlePageChange = async (p: number) => {
setPage(p);
setLoading(true);
fetchBookings(bookingFilter, p, PAGE_SIZE).then((result) => {
setTotal(result.total);
setBookings(result.bookings);
setLoading(false);
});
};

const filteredBookings = bookings.filter((b) => {
const mq =
b.client.toLowerCase().includes(bookingSearch.toLowerCase()) ||
b.venue.toLowerCase().includes(bookingSearch.toLowerCase()) ||
b.id.toLowerCase().includes(bookingSearch.toLowerCase());
return mq;
});

const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
const pagedBookings = filteredBookings;

const getPageNumbers = () => {
if (totalPages <= 5) return Array.from({ length: totalPages }, (_, i) => i + 1);
if (page <= 3) return [1, 2, 3, 4, null, totalPages];
if (page >= totalPages - 2)
return [1, null, totalPages - 3, totalPages - 2, totalPages - 1, totalPages];
return [1, null, page - 1, page, page + 1, null, totalPages];
};

return (
<div className="bg-card border border-border rounded-2xl overflow-hidden">
<div className="px-5 py-4 border-b border-border flex flex-col sm:flex-row gap-3">
<div className="relative flex-1">
<Search className="absolute left-3 top-2.5 w-4 h-4 text-muted-foreground" />
<input
className="w-full pl-9 pr-4 py-2 bg-input-background border border-border rounded-xl text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
placeholder="Search by client, venue, booking ID…"
value={bookingSearch}
onChange={(e) => setBookingSearch(e.target.value)}
/>
</div>
<div className="flex gap-2">
{bookingFilters.map((s) => (
<button
key={s}
onClick={() => handleFilterChange(s)}
className={`px-3 py-2 rounded-xl text-xs font-semibold transition-all capitalize ${bookingFilter === s ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-secondary"}`}
>
{s.toLocaleLowerCase()}
</button>
))}
</div>
<button className="flex items-center gap-1.5 px-3 py-2 rounded-xl border border-border text-xs font-semibold text-muted-foreground hover:bg-muted transition-colors shrink-0">
<Download className="w-3.5 h-3.5" /> Export
</button>
</div>

<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
{[
"Booking ID",
"Client",
"Venue",
"Owner",
"Date",
"Category",
"Amount",
"Status",
].map((h) => (
<TableHead
key={h}
className="px-5 py-3 text-xs font-bold text-muted-foreground uppercase tracking-wider"
>
{h}
</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{loading
? Array.from({ length: 6 }).map((_, i) => (
<TableRow key={i}>
{Array.from({ length: 8 }).map((_, j) => (
<TableCell key={j}>
<Skeleton className="h-10 w-full" />
</TableCell>
))}
</TableRow>
))
: pagedBookings.map((b) => (
<TableRow key={b.id} className="hover:bg-muted/30">
<TableCell className="px-5 py-3.5 font-mono text-xs text-muted-foreground">
{b.id}
</TableCell>
<TableCell className="px-5 py-3.5 font-semibold text-foreground capitalize">
{b.client}
</TableCell>
<TableCell className="px-5 py-3.5 text-foreground/80">
{b.venue}
</TableCell>
<TableCell className="px-5 py-3.5 text-foreground/70 capitalize">
{b.owner}
</TableCell>
<TableCell className="px-5 py-3.5 text-foreground/70">
{b.date}
</TableCell>
<TableCell className="px-5 py-3.5 text-foreground/70">
{b.category}
</TableCell>
<TableCell className="px-5 py-3.5 font-bold text-foreground">
{fmtAmount(b.amount)}
</TableCell>
<TableCell className="px-5 py-3.5">
<span
className={`text-xs font-semibold px-2.5 py-1 rounded-full border ${BOOKING_STATUS_STYLE[b.status]}`}
>
{b.status}
</span>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{!loading && pagedBookings.length === 0 && (
<div className="text-center py-16 text-muted-foreground">
<CalendarCheck className="w-10 h-10 mx-auto mb-3 opacity-25" />
<p className="font-medium">No bookings match your filter.</p>
</div>
)}
</div>

<div className="px-5 py-3.5 border-t border-border flex items-center justify-between text-xs text-muted-foreground">
<span>
Showing {total === 0 ? 0 : (page - 1) * PAGE_SIZE + 1}–{Math.min(page * PAGE_SIZE, total)}{" "}
of {total} bookings
</span>
<Pagination className="w-auto mx-0">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
onClick={(e) => {
e.preventDefault();
handlePageChange(Math.max(1, page - 1));
}}
aria-disabled={page === 1}
className={page === 1 ? "pointer-events-none opacity-50" : ""}
/>
</PaginationItem>
{getPageNumbers().map((n, i) =>
n === null ? (
<PaginationItem key={`ellipsis-${i}`}>
<PaginationEllipsis />
</PaginationItem>
) : (
<PaginationItem key={n}>
<PaginationLink
href="#"
isActive={n === page}
onClick={(e) => {
e.preventDefault();
handlePageChange(n);
}}
>
{n}
</PaginationLink>
</PaginationItem>
),
)}
<PaginationItem>
<PaginationNext
href="#"
onClick={(e) => {
e.preventDefault();
handlePageChange(Math.min(totalPages, page + 1));
}}
aria-disabled={page === totalPages}
className={page === totalPages ? "pointer-events-none opacity-50" : ""}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
</div>
);
}
33 changes: 33 additions & 0 deletions apps/admin/app/(admin)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use client";

import { useState } from "react";
import { Sidebar } from "../../components/Sidebar";
import { TopBar } from "../../components/TopBar";

export default function AdminLayout({ children }: { children: React.ReactNode }) {
const [sidebarOpen, setSidebarOpen] = useState(false);

return (
<div className="h-screen flex overflow-hidden bg-background">
<div className="w-56 shrink-0 hidden lg:block">
<Sidebar setSidebarOpen={setSidebarOpen} />
</div>

{sidebarOpen && (
<div className="fixed inset-0 z-50 flex lg:hidden">
<div className="absolute inset-0 bg-black/50" onClick={() => setSidebarOpen(false)} />
<div className="relative w-56 h-full shadow-2xl">
<Sidebar setSidebarOpen={setSidebarOpen} mobile />
</div>
</div>
)}

<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
<TopBar setSidebarOpen={setSidebarOpen} />
<main className="flex-1 overflow-y-auto px-4 sm:px-6 py-6">
{children}
</main>
</div>
</div>
);
}
5 changes: 5 additions & 0 deletions apps/admin/app/(admin)/users/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { UsersPage as UsersTab } from "../../../components/tabs/Users";

export default function UsersPage() {
return <UsersTab />;
}
5 changes: 5 additions & 0 deletions apps/admin/app/(admin)/venues/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { VenuesPage as VenuesTab } from "../../../components/tabs/Venues";

export default function VenuesPage() {
return <VenuesTab />;
}
Loading