Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
__pycache__/
*.pyc
.env
.venv/
.idea/
.vscode/
.DS_Store
node_modules/
.coverage
htmlcov/
pytest_cache/
140 changes: 112 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,131 @@
# Sensasiwangi.id MVP Workspace

Repositori ini memuat dokumen awal pengembangan untuk proyek **Sensasiwangi.id**, platform wewangian lokal yang dijelaskan pada dokumen [PRD_MVP.md](PRD_MVP.md).
Repositori ini memuat dokumen dan kode awal pengembangan untuk proyek
**Sensasiwangi.id**, platform wewangian lokal yang dijelaskan pada dokumen
[PRD_MVP.md](PRD_MVP.md).

## Arsitektur Aplikasi

MVP dibangun menggunakan FastAPI dengan rendering server-side (SSR) melalui
Jinja2 dan integrasi HTMX untuk interaksi progresif sesuai PRD. Struktur modul
utama:

```
src/app/
├── api/ # Router HTTP dan handler tampilan
├── core/ # Konfigurasi aplikasi & factory
├── services/ # Tempat logika domain (akan diisi bertahap)
└── web/
├── static/ # Asset glassmorphism (CSS, ikon, dsb.)
└── templates/ # Template Jinja2 + partial navbar/footer
```

`create_app` pada `src/app/core/application.py` menyiapkan middleware, static
files, dan registrasi router untuk halaman landing awal.

## Menjalankan Secara Lokal

1. Buat virtualenv dan instal dependensi:

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e .[dev]
```

2. Jalankan server pengembangan dengan Uvicorn:

```bash
uvicorn app.main:app --reload
```

3. Akses `http://localhost:8000` untuk melihat landing page awal bertema
glassmorphism.

## Pengujian

Gunakan `pytest` untuk menjalankan test dasar yang memverifikasi homepage SSR:

```bash
pytest
```

## To-Do List Implementasi Berdasarkan PRD
Daftar berikut dirinci mengikuti fase prioritas dan milestone yang disebutkan di PRD. Checklist dapat disesuaikan selama sprint planning, namun urutan menjaga dependensi utama.

Daftar berikut dirinci mengikuti fase prioritas dan milestone yang disebutkan di
PRD. Checklist dapat disesuaikan selama sprint planning, namun urutan menjaga
dependensi utama.

### Fase Fondasi Teknis (Minggu 1-2)
- [ ] Finalisasi skema data inti di Supabase untuk modul: pesanan, produk (termasuk atribut Sambatan), brand/etalase, artikel Nusantarum, profil pengguna, dan relasi user-brand.
- [ ] Provisioning proyek Supabase (staging & production), termasuk konfigurasi Auth (magic link), Storage bucket per brand/artikel, dan variabel lingkungan (`SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`).
- [ ] Menyiapkan repositori kode backend (FastAPI + HTMX/Jinja2) dengan struktur modul sesuai layanan yang dibutuhkan (OrderService, ProductService, SambatanLifecycle, dsb.).
- [ ] Menyiapkan panduan desain awal: wireframe navigasi utama (Dashboard, Marketplace, Nusantarum, Profil) dan style tile bertema glassmorphism (rujuk `docs/guideline-glassmorphism.md` ketika tersedia).
- [ ] Menentukan SOP verifikasi merchant pilot & kurasi konten awal sebagai input onboarding.
- [ ] Finalisasi skema data inti di Supabase untuk modul: pesanan, produk
(termasuk atribut Sambatan), brand/etalase, artikel Nusantarum, profil
pengguna, dan relasi user-brand.
- [ ] Provisioning proyek Supabase (staging & production), termasuk konfigurasi
Auth (magic link), Storage bucket per brand/artikel, dan variabel
lingkungan (`SUPABASE_URL`, `SUPABASE_ANON_KEY`,
`SUPABASE_SERVICE_ROLE_KEY`).
- [ ] Menyiapkan repositori kode backend (FastAPI + HTMX/Jinja2) dengan struktur
modul sesuai layanan yang dibutuhkan (OrderService, ProductService,
SambatanLifecycle, dsb.).
- [ ] Menyiapkan panduan desain awal: wireframe navigasi utama (Dashboard,
Marketplace, Nusantarum, Profil) dan style tile bertema glassmorphism
(rujuk `docs/guideline-glassmorphism.md` ketika tersedia).
- [ ] Menentukan SOP verifikasi merchant pilot & kurasi konten awal sebagai
input onboarding.

### Fase Pesanan & Operasional (Minggu 3-4)
- [ ] Implementasi alur pembuatan pesanan: form produk, kuantitas, alamat RajaOngkir, jadwal pengiriman, dan status awal `Draf`.
- [ ] Membangun dashboard operasional internal untuk memperbarui status pesanan, menambahkan nomor resi, dan mencatat audit trail.
- [ ] Implementasi alur pembuatan pesanan: form produk, kuantitas, alamat
RajaOngkir, jadwal pengiriman, dan status awal `Draf`.
- [ ] Membangun dashboard operasional internal untuk memperbarui status pesanan,
menambahkan nomor resi, dan mencatat audit trail.
- [ ] Implementasi ekspor CSV laporan pesanan harian.
- [ ] Membuat profil brand internal (form data brand, sertifikasi, kontak) serta relasi user-brand (owner/admin/kontributor).
- [ ] Integrasi API RajaOngkir untuk validasi alamat dan estimasi ongkir saat checkout internal.
- [ ] Membuat profil brand internal (form data brand, sertifikasi, kontak) serta
relasi user-brand (owner/admin/kontributor).
- [ ] Integrasi API RajaOngkir untuk validasi alamat dan estimasi ongkir saat
checkout internal.

### Fase Marketplace & Brand Publik (Minggu 5)
- [ ] Mengimplementasikan halaman marketplace dengan empat tab kategori (Parfum, Raw Material, Tools, Lainnya), pencarian teks per tab, dan filter aroma tambahan.
- [ ] Menampilkan detail produk minimum (nama, harga indikatif, stok, foto utama, highlight aroma) dengan dukungan mode Sambatan.
- [ ] Menyelesaikan profil brand publik: deskripsi singkat, katalog produk, story Nusantara, sertifikasi, tautan artikel Nusantarum, dan CTA kontak.
- [ ] Menyiapkan grid listing Sambatan beserta indikator progress bar dan countdown di halaman produk.
- [ ] Mengimplementasikan halaman marketplace dengan empat tab kategori
(Parfum, Raw Material, Tools, Lainnya), pencarian teks per tab, dan filter
aroma tambahan.
- [ ] Menampilkan detail produk minimum (nama, harga indikatif, stok, foto
utama, highlight aroma) dengan dukungan mode Sambatan.
- [ ] Menyelesaikan profil brand publik: deskripsi singkat, katalog produk,
story Nusantara, sertifikasi, tautan artikel Nusantarum, dan CTA kontak.
- [ ] Menyiapkan grid listing Sambatan beserta indikator progress bar dan
countdown di halaman produk.

### Fase Nusantarum & Konten (Minggu 6)
- [ ] Mengembangkan halaman Nusantarum dengan highlight hero dan tiga tab (Parfum, Brand, Perfumer) plus panel filter kaca (kategori aroma, wilayah, kurator, status linked) dan pencarian teks.
- [ ] Membangun panel manajemen konten kurator untuk menambah entri, menandai status verifikasi, serta melakukan linking ke profil terkait.
- [ ] Menautkan artikel Nusantarum ke produk/brand/perfumer relevan guna mengarahkan traffic.
- [ ] Mengembangkan halaman Nusantarum dengan highlight hero dan tiga tab
(Parfum, Brand, Perfumer) plus panel filter kaca (kategori aroma, wilayah,
kurator, status linked) dan pencarian teks.
- [ ] Membangun panel manajemen konten kurator untuk menambah entri, menandai
status verifikasi, serta melakukan linking ke profil terkait.
- [ ] Menautkan artikel Nusantarum ke produk/brand/perfumer relevan guna
mengarahkan traffic.

### Fase Profil Pengguna & Sambatan (Minggu 7)
- [ ] Membuat profil pengguna marketplace (nama, email, preferensi aroma) dan fitur favorit sederhana.
- [ ] Implementasi alur Sambatan: create request (`is_sambatan`, `total_slots`, `sambatan_deadline`), join request dengan transaksi aman, progress otomatis (`remaining_slots`, `sambatan_progress_percentage`).
- [ ] Mengembangkan `SambatanLifecycleService` & scheduler untuk menangani deadline, payout/refund, serta logging ke `SambatanAuditLog`.
- [ ] Membuat dashboard seller Sambatan dengan statistik slot, kontribusi terakhir, dan kontrol status.
- [ ] Membuat profil pengguna marketplace (nama, email, preferensi aroma) dan
fitur favorit sederhana.
- [ ] Implementasi alur Sambatan: create request (`is_sambatan`, `total_slots`,
`sambatan_deadline`), join request dengan transaksi aman, progress otomatis
(`remaining_slots`, `sambatan_progress_percentage`).
- [ ] Mengembangkan `SambatanLifecycleService` & scheduler untuk menangani
deadline, payout/refund, serta logging ke `SambatanAuditLog`.
- [ ] Membuat dashboard seller Sambatan dengan statistik slot, kontribusi
terakhir, dan kontrol status.

### Fase QA, Peluncuran, & Operasional (Minggu 8)
- [ ] Menjalankan suite QA lintas modul: pytest (async + coverage), linting Ruff & Black, dan verifikasi migrasi Supabase.
- [ ] Melaksanakan UAT internal dengan skenario marketplace, Nusantarum, dan Sambatan (termasuk linking ulang oleh kurator).
- [ ] Menyusun SOP operasional akhir: respon tiket Sambatan, fallback manual refund/payout, dan monitoring log dasar.
- [ ] Menyiapkan deployment ke Vercel atau alternatif kontainer (Gunicorn + Uvicorn) dan strategi rollback.
- [ ] Mengaktifkan monitoring dasar serta menyiapkan checklist post-mortem jika terjadi kegagalan besar.
- [ ] Menjalankan suite QA lintas modul: pytest (async + coverage), linting Ruff
& Black, dan verifikasi migrasi Supabase.
- [ ] Melaksanakan UAT internal dengan skenario marketplace, Nusantarum, dan
Sambatan (termasuk linking ulang oleh kurator).
- [ ] Menyusun SOP operasional akhir: respon tiket Sambatan, fallback manual
refund/payout, dan monitoring log dasar.
- [ ] Menyiapkan deployment ke Vercel atau alternatif kontainer (Gunicorn +
Uvicorn) dan strategi rollback.
- [ ] Mengaktifkan monitoring dasar serta menyiapkan checklist post-mortem jika
terjadi kegagalan besar.

### Backlog Pasca-MVP
- [ ] Notifikasi otomatis (email/push) untuk event pesanan dan Sambatan.
Expand All @@ -50,5 +134,5 @@ Daftar berikut dirinci mengikuti fase prioritas dan milestone yang disebutkan di
- [ ] Aplikasi mobile native dan multi-bahasa.

## Referensi
- [PRD Sensasiwangi.id](PRD_MVP.md)

- [PRD Sensasiwangi.id](PRD_MVP.md)
52 changes: 52 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[build-system]
requires = ["setuptools>=67", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "sensasiwangi"
version = "0.1.0"
description = "MVP web application for Sensasiwangi.id"
authors = [{ name = "Sensasiwangi Team" }]
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.110",
"uvicorn[standard]>=0.27",
"jinja2>=3.1",
"python-multipart>=0.0.6",
"pydantic-settings>=2.2",
"aiofiles>=23.2",
]

[project.optional-dependencies]
dev = [
"pytest>=8.0",
"httpx>=0.27",
"anyio>=4.0",
]

[tool.black]
line-length = 88
target-version = ["py311"]

[tool.ruff]
line-length = 88
src = ["src", "tests"]
select = ["E", "F", "I", "UP", "B", "A", "COM", "C4"]
ignore = ["E501"]

[tool.setuptools]
package-dir = {"" = "src"}

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
"app" = [
"web/templates/**/*.html",
"web/static/**/*",
]

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
Empty file added src/app/__init__.py
Empty file.
Empty file added src/app/api/__init__.py
Empty file.
Empty file added src/app/api/routes/__init__.py
Empty file.
22 changes: 22 additions & 0 deletions src/app/api/routes/root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Landing page routes for the MVP."""

from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse

from app.core.config import get_settings

router = APIRouter()


@router.get("/", response_class=HTMLResponse)
async def read_home(request: Request) -> HTMLResponse:
"""Render the marketplace landing page placeholder."""

settings = get_settings()
templates = request.app.state.templates
context = {
"request": request,
"app_name": settings.app_name,
"environment": settings.environment,
}
return templates.TemplateResponse("index.html", context)
Empty file added src/app/core/__init__.py
Empty file.
41 changes: 41 additions & 0 deletions src/app/core/application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Application factory for the Sensasiwangi.id MVP."""

from pathlib import Path

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from starlette.staticfiles import StaticFiles

from app.core.config import get_settings
from app.web.templates import template_engine
from app.api.routes import root as root_routes

STATIC_DIR = Path(__file__).resolve().parent.parent / "web" / "static"


def create_app() -> FastAPI:
"""Create and configure the FastAPI application instance."""

settings = get_settings()

app = FastAPI(title=settings.app_name)

# Basic CORS setup to simplify early integrations with Supabase and
# front-end previews during the MVP stage.
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)

# Mount static assets (CSS, JS, images) served by the Jinja2 templates.
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")

# Register routers for server-rendered pages.
app.include_router(root_routes.router)

# Expose the template engine on the app state for reuse by routers.
app.state.templates = template_engine

return app
33 changes: 33 additions & 0 deletions src/app/core/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Application configuration module."""

from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
"""Centralized application settings loaded from environment variables.

The fields reflect integrations highlighted in the PRD such as Supabase and
RajaOngkir. Additional configuration values can be appended as features are
implemented.
"""

app_name: str = "Sensasiwangi.id"
environment: str = "development"

# Supabase configuration
supabase_url: str | None = None
supabase_anon_key: str | None = None
supabase_service_role_key: str | None = None

# RajaOngkir integration
rajaongkir_api_key: str | None = None

model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")


@lru_cache
def get_settings() -> Settings:
"""Return cached settings instance."""

return Settings()
5 changes: 5 additions & 0 deletions src/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Entry point for ASGI servers."""

from app.core.application import create_app

app = create_app()
Empty file added src/app/services/__init__.py
Empty file.
Loading