AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Projects
  3. Picco Studio

Picco Studio

October 5, 20245 min read
View ProjectRepository
Next JsTypescriptTailwind CSSXenditPrismicFirebase
1 / 3

Overview

Picco Studio is the customer-facing booking platform for picco.id, an Indonesian self-service photography studio chain. Visitors pick a location, room, background, package, and photo session time slot, then pay online to reserve the studio — without needing to call or message staff.

The site has two halves. The public side is a Prismic-powered marketing and booking flow (locations, rooms, packages, products, and a portfolio "story" gallery). The authenticated side is a Firebase-backed account area with a user dashboard for orders and loyalty points, and an admin/operator dashboard for managing schedules, orders, and order confirmations.

Payments run through Xendit Invoices: the browser never touches Xendit directly, only a Next.js API route that proxies to the Xendit API with the server-side secret key. All booking and loyalty state lives in Firebase Firestore, keyed by Firebase Auth users who sign in with Google.

Tech Stack & Rationale

Layer

Technology

Why

Framework

Next.js 15 (App Router), React 18

Server-rendered Prismic pages plus client-side booking and dashboards

Language

TypeScript

Strict types for Prismic documents, Firestore order shapes, and Xendit responses

Styling

Tailwind CSS 3.4

Studio brand styling across slices and booking steps

CMS

Prismic + Slice Machine

Editors manage locations, rooms, packages, products, and stories

Auth & data

Firebase (Auth, Firestore, Storage)

Google sign-in, order/point records, and photo uploads

Payments

Xendit Invoices API

Invoice + payment-link checkout via a server-side proxy route

Client state

Redux Toolkit

Booking wizard (location → checkout) and auth/customer slices

Forms

Formik + Yup

Checkout and contact form validation

HTTP

Axios

Wrappers for the Picco API routes and Xendit

Monitoring

Sentry

Production error tracking

Analytics

Vercel Analytics + Speed Insights

Traffic and performance visibility

Architecture

A booking is a multi-step wizard whose state lives in the Redux booking slice, persisted to Firestore only at the final step. The browser calls same-origin API routes that wrap Firebase Admin and Xendit, keeping both secret keys server-side.

The flow runs: Booking wizard (location → checkout) → Redux booking slice (location · session · extras) → Firebase Auth (Google sign-in) → /api/firebase/* (orders · points · promo) → Firestore (users · orders); Booking wizard (location → checkout) → Redux booking slice (location · session · extras) → /api/xendit/create-invoices (Bearer token proxy) → Xendit (/v2/invoices).

  1. Booking wizard — the visitor steps through LocationStep, RoomStep, DateStep, SessionStep, BackgroundStep, PackageStep, and ExtraStep. Each step reads Prismic documents (via core/prismic/fetcher.ts) and writes to the Redux booking slice.
  2. Checkout — the CheckoutStep validates the form with Formik/Yup, applies promo codes, referral codes, and loyalty points (fetched through /api/firebase/check-promo-code, check-referral-code, and the point endpoints), then computes the total.
  3. Payment — core/utils/xendit/payment.ts POSTs to /api/xendit/create-invoices with the Firebase ID token; the route verifies the token in authMiddleware, then calls Xendit POST /v2/invoices using the server-side XENDIT_SECRET_KEY, returning the invoice and payment link.
  4. Order persistence — the client writes the order to Firestore through /api/firebase/set-room-order (or set-extra-order / set-product-order), storing payment id, link, expiry, and status.
  5. Confirmation & loyalty — staff confirm orders via the admin dashboard (change-order-status, change-room-order-schedule); users submit post-visit order confirmations and earn/transfer points through the points, referrals, and promo_codes collections.

Folder Structure

picco-studio-web/
├── app/
│ ├── (routes)/(main)/(account)/(dashboard)/ # admin / operator / user dashboards + login
│ ├── (routes)/(main)/(booking)/booking/ # booking wizard entry
│ ├── (routes)/(prismic)/ # [[...slug]], announcement/[uid], package/[uid], product/[uid]
│ ├── api/ # xendit/create-invoices, firebase/*, auth, revalidate, preview
│ └── slice-simulator/ · sentry-example-page/
├── components/ # Announcement, Auth, Booking, Package, Product, User, _shared
├── core/
│ ├── cloud/ # picco.ts, xendit.ts (Axios wrappers)
│ ├── data/ # BookingData, SpaceData, ThemeData
│ ├── hooks/ # useAuth, useAutoDispatcher, useResize, useScrollDirection
│ ├── lib/ # firebaseAdmin.ts, firebaseConfig.ts
│ ├── prismic/ # fetcher.ts
│ ├── redux/ # slices + actions (auth, booking, customer)
│ ├── utils/ # firebase/*, xendit/*, general/*
│ └── @types/ # auth, booking, customer, firebase, xendit, prismic
├── slices/ # 15 slices (Hero, PackageList, Photobooth, PaymentMethod, …)
├── customtypes/ # location, room, background, package, product, extra, story, …
├── constants/ # holidays.ts
├── styles/ # globals, calendar, pagination, StorySwiper
├── public/ # assets, robots.txt, sitemap
├── prismicio.ts · firestore.indexes.json · firebase.json · next.config.mjs
└── tailwind.config.ts · tsconfig.json · package.json

Database Design

Firestore is the transactional store. Seven top-level collections back the ordering and loyalty features:

Collection

Purpose

users

Firebase Auth profiles — uid, display_name, role_id, is_active, timestamps

orders

All orders (room, extra, product types) — payment id/link, status, total_payment, nested order_data

order_confirmations

Post-visit ratings and feedback — rating, feedback, order_id, type

points

Loyalty balances and history — total_point, history[] with in/out entries

referrals

Referral codes — code, user_id, quantity, expired

promo_codes

Promotional discounts — code, discount_percentage, description

products

Physical product/print stock — locations with variation quantities

Composite indexes for the orders, order_confirmations, and users collections are declared in firestore.indexes.json (filtering by status, type, user_id, and created_at).

Content Model

Prismic supplies all catalog and marketing content. The booking wizard resolves relationships between custom types — a location links to its rooms, each room links to backgrounds, packages, and extras, and the portfolio gallery links to story documents.

Custom type

Kind

Purpose

location

Repeatable

Studio branches — linked rooms and products, SEO

room

Repeatable

Photo studio rooms — linked backgrounds, packages, extras

background

Repeatable

Backdrop choices within a room

package

Repeatable

Photo session packages with benefits and pricing

product

Repeatable

Prints and frames with variations

extra

Repeatable

Add-on items (e.g. passport photo) with quantity and price

story

Repeatable

Portfolio gallery items for the showcase

booking_step

Repeatable

Ordered onboarding/step content for the booking wizard

announcement

Repeatable

Site announcements with SEO metadata

page / layout / frame

—

Generic page chrome and frame content

Impact & Results

  • In production for picco.id — an Indonesian self-service photography studio chain — handling bookings, online payment (Xendit), and operator schedules.
  • Self-service booking removes the need for customers to call or message staff to reserve a studio.

My Contributions

Built on the Picco Studio booking platform as the lead frontend developer:

  • I built the multi-step booking wizard (Location → Room → Date → Session → Background → Package → Extra → Checkout) driven by the Redux booking slice and Prismic relationship fetchers.
  • I implemented the Xendit payment flow through the /api/xendit/create-invoices proxy route, keeping the secret key server-side and using the Firebase ID token for auth.
  • I implemented Firebase Firestore order persistence and the admin/operator/user dashboards for schedules, orders, and order confirmations.
  • I built the loyalty system — points, referral codes, and promo codes — including point transfer between users and the admin point management page.
  • I added the Photobooth component with authentication protection, plus the passport photo (pas foto) package and the print feature.
  • I optimized admin user pagination with cursor-based Firestore queries and caching.
  • I added special-hours and holiday handling to the booking calendar and time-slot generation.

References

[1] Picco Studio, "Picco Studio," picco.id, 2024. [Online]. Available: https://picco.id

[2] Vercel, "Next.js Documentation," nextjs.org. [Online]. Available: https://nextjs.org/docs

[3] Prismic, "Prismic Documentation," prismic.io. [Online]. Available: https://prismic.io/docs

[4] Google, "Firebase Documentation," firebase.google.com. [Online]. Available: https://firebase.google.com/docs

[5] Xendit, "Xendit API Reference," developers.xendit.co. [Online]. Available: https://developers.xendit.co/api-reference

[6] Redux Toolkit, "Redux Toolkit Documentation," redux-toolkit.js.org. [Online]. Available: https://redux-toolkit.js.org

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!