-
-
Habib Marriage
-
- Screen previews for the app flow
-
-
- The non-intro screens now share one warmer fabric-inspired visual
- system. Use this page to move through each route while checking
- the updated flow.
-
-
-
-
-
- Surfaces
-
-
- Unified cards, inputs, and action buttons across the flow.
-
-
-
-
- Navigation
-
-
- Shared headers, progress states, and clearer completion cues.
-
-
-
-
-
-
-
- Routes
-
-
- {previews.map((preview) => (
-
-
-
-
- {preview.title}
-
-
- {preview.description}
-
-
-
- Open
-
-
-
- ))}
-
-
+
+
+
+
+
+ To get started, edit the page.tsx file.
+
+
+ Looking for a starting point or more instructions? Head over to{" "}
+
+ Templates
+ {" "}
+ or the{" "}
+
+ Learning
+ {" "}
+ center.
+
+
+
-
-
+
+
);
}
diff --git a/src/app/questions/page.tsx b/src/app/questions/page.tsx
deleted file mode 100644
index 06fa5ca..0000000
--- a/src/app/questions/page.tsx
+++ /dev/null
@@ -1,209 +0,0 @@
-"use client";
-
-import Link from "next/link";
-import { useRouter } from "next/navigation";
-import { useState } from "react";
-import {
- BackIcon,
- FabricCard,
- FabricIconButton,
- FabricProgress,
- FabricScreen,
- FabricStatusBar,
- fabricInputClass,
- fabricMutedPanelClass,
- fabricPrimaryButtonClass,
- fabricTextareaClass,
-} from "@/components/ui/fabric-mobile";
-
-const slides = [
- {
- helperText: "Enter your age in years.",
- helper: "Question 1",
- inputType: "number",
- placeholder: "Type your age",
- question: "How old are you?",
- title: "Let us begin with your age",
- },
- {
- helperText: "A short description is enough for now.",
- helper: "Question 2",
- inputType: "textarea",
- placeholder:
- "Write a few lines about yourself, your goals, or your family values",
- question:
- "How would you introduce yourself for a serious Islamic marriage?",
- title: "Share a brief introduction",
- },
- {
- helperText: "Use your actual date of birth.",
- helper: "Question 3",
- inputType: "date",
- question: "What is your date of birth?",
- title: "Confirm your birth date",
- },
-] as const;
-
-export default function QuestionsPage() {
- const [currentSlide, setCurrentSlide] = useState(0);
- const [answers, setAnswers] = useState
>({});
- const router = useRouter();
- const lastSlideIndex = slides.length - 1;
- const activeSlide = slides[currentSlide];
- const currentAnswer = answers[currentSlide] ?? "";
- const hasAnswer = currentAnswer.trim().length > 0;
- const progressPercent = ((currentSlide + 1) / slides.length) * 100;
-
- const handleChange = (value: string) => {
- setAnswers((currentAnswers) => ({
- ...currentAnswers,
- [currentSlide]: value,
- }));
- };
-
- const handleBack = () => {
- setCurrentSlide((previousSlide) => Math.max(previousSlide - 1, 0));
- };
-
- const handleNext = () => {
- if (!hasAnswer) {
- return;
- }
-
- setCurrentSlide((previousSlide) =>
- Math.min(previousSlide + 1, lastSlideIndex),
- );
- };
-
- const handleFinish = () => {
- if (!hasAnswer) {
- return;
- }
-
- router.push("/details");
- };
-
- return (
-
-
-
-
-
-
-
-
-
Step 4
-
- {activeSlide.title}
-
-
-
- {currentSlide + 1} / {slides.length}
-
-
-
-
- {activeSlide.question}
-
-
- {activeSlide.helperText}
-
-
-
-
-
-
{activeSlide.helper}
-
- Required
-
-
-
-
- {activeSlide.inputType === "number" ? (
- handleChange(event.target.value)}
- placeholder={activeSlide.placeholder}
- type="number"
- value={currentAnswer}
- />
- ) : null}
-
- {activeSlide.inputType === "textarea" ? (
-
-
-
-
-
- Current answer
-
-
- {currentAnswer || "Complete the field above to continue."}
-
-
-
-
- {currentSlide === lastSlideIndex ? (
-
- Finish
-
- ) : (
-
- Next
-
- )}
-
-
- );
-}
diff --git a/src/app/rules/page.tsx b/src/app/rules/page.tsx
deleted file mode 100644
index 2a3d856..0000000
--- a/src/app/rules/page.tsx
+++ /dev/null
@@ -1,163 +0,0 @@
-"use client";
-
-import Link from "next/link";
-import { useEffect, useState } from "react";
-import {
- BackIcon,
- FabricCard,
- FabricIconLink,
- FabricPill,
- FabricScreen,
- FabricStatusBar,
- fabricDisabledLinkClass,
- fabricMutedPanelClass,
- fabricSecondaryButtonClass,
-} from "@/components/ui/fabric-mobile";
-
-const waitDuration = 20;
-
-export default function RulesPage() {
- const [secondsLeft, setSecondsLeft] = useState(waitDuration);
-
- useEffect(() => {
- if (secondsLeft <= 0) {
- return;
- }
-
- const timer = window.setInterval(() => {
- setSecondsLeft((currentValue) => Math.max(currentValue - 1, 0));
- }, 1000);
-
- return () => window.clearInterval(timer);
- }, [secondsLeft]);
-
- const isEnabled = secondsLeft === 0;
-
- return (
-
-
-
-
-
-
-
-
-
-
Step 2
-
- Terms & Values
-
-
-
-
- {secondsLeft}s
-
-
-
-
-
-
-
Before you continue
-
- Read the platform guidelines once
-
-
-
Step 2
-
-
-
- Habib Marriage is built for permanent Islamic marriage with privacy,
- identity verification, and family involvement at the center.
-
-
-
-
-
-
-
- Dating, casual browsing, and public catalogs are not part of this
- process. Introductions are deliberate and one-to-one.
-
-
-
-
-
-
-
-
- 01
-
-
-
- A secure and purposeful path to permanent union
-
-
-
- This is not a typical matchmaking feed. The goal is a
- confidential path toward permanent marriage among Muslims.
-
-
- To protect dignity, profiles are not publicly listed and
- identity checks are required for everyone who participates.
-
-
- Family and guardian involvement are treated as part of the
- process rather than an optional afterthought.
-
-
-
-
-
-
-
-
- Privacy
-
- No public catalog of candidates.
-
-
-
-
- Verification
-
- Identity checks are part of the process.
-
-
-
-
- Intention
-
- Permanent marriage only, not casual dating.
-
-
-
-
- Family
-
- Wali and family participation are expected.
-
-
-
-
-
-
-
- {isEnabled
- ? "You can continue to the next step."
- : `The continue button unlocks in ${secondsLeft} seconds so the guidelines stay visible.`}
-
-
-
- {isEnabled ? "Next" : `Next in ${secondsLeft}s`}
-
-
-
- );
-}
diff --git a/src/app/video-2/page.tsx b/src/app/video-2/page.tsx
deleted file mode 100644
index e1a4993..0000000
--- a/src/app/video-2/page.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import VideoStepScreen from "@/components/screens/video-step-screen";
-
-export default function VideoPageTwo() {
- return (
-
- );
-}
diff --git a/src/app/video/page.tsx b/src/app/video/page.tsx
deleted file mode 100644
index e9f65e4..0000000
--- a/src/app/video/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import VideoStepScreen from "@/components/screens/video-step-screen";
-
-export default function VideoPage() {
- return ;
-}
diff --git a/src/components/dev/dev-click-to-component.tsx b/src/components/dev/dev-click-to-component.tsx
deleted file mode 100644
index 7d020f4..0000000
--- a/src/components/dev/dev-click-to-component.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-"use client";
-
-import { useEffect } from "react";
-
-const IDE_SCHEMES: Record string> = {
- antigravity: (locator) => `antigravity://file/${locator}`,
- cursor: (locator) => `cursor://file/${locator}`,
- vscode: (locator) => `vscode://file/${locator}`,
- webstorm: (locator) => `webstorm://open?file=${locator}`,
- sublime: (locator) => `subl://open?url=file://${locator}`,
- atom: (locator) => `atom://open?url=file://${locator}`,
-};
-
-function resolveIdeUrl(locator: string) {
- const preferredIde =
- process.env.NEXT_PUBLIC_CLICK_TO_COMPONENT_IDE?.toLowerCase() ?? "vscode";
-
- const userAgent = navigator.userAgent.toLowerCase();
- const detectedIde =
- (Object.keys(IDE_SCHEMES).find((ide) => userAgent.includes(ide)) as
- | keyof typeof IDE_SCHEMES
- | undefined) ?? preferredIde;
-
- const scheme =
- IDE_SCHEMES[detectedIde] ?? IDE_SCHEMES[preferredIde] ?? IDE_SCHEMES.vscode;
-
- return scheme(locator);
-}
-
-export function DevClickToComponent() {
- useEffect(() => {
- const handleClick = (event: MouseEvent) => {
- if (!event.altKey) {
- return;
- }
-
- event.preventDefault();
- event.stopPropagation();
-
- const target = event.target;
- if (!(target instanceof HTMLElement)) {
- return;
- }
-
- const locator = target
- .closest("[data-locator]")
- ?.getAttribute("data-locator");
-
- if (!locator) {
- return;
- }
-
- try {
- window.location.assign(resolveIdeUrl(locator));
- } catch (error) {
- console.error("Failed to open file in IDE:", error);
- }
- };
-
- document.addEventListener("click", handleClick, true);
-
- return () => {
- document.removeEventListener("click", handleClick, true);
- };
- }, []);
-
- return null;
-}
diff --git a/src/components/screens/video-step-screen.tsx b/src/components/screens/video-step-screen.tsx
deleted file mode 100644
index 7c1ec08..0000000
--- a/src/components/screens/video-step-screen.tsx
+++ /dev/null
@@ -1,202 +0,0 @@
-"use client";
-
-import Image from "next/image";
-import Link from "next/link";
-import { useEffect, useState } from "react";
-import {
- BackIcon,
- FabricCard,
- FabricIconButton,
- FabricIconLink,
- FabricPill,
- FabricProgress,
- FabricScreen,
- FabricStatusBar,
- fabricDisabledLinkClass,
- fabricMutedPanelClass,
- fabricSecondaryButtonClass,
- HistoryIcon,
- PlayIcon,
-} from "@/components/ui/fabric-mobile";
-
-const metrics = [
- { label: "Marriage applicants", value: "120" },
- { label: "Successful marriages", value: "120" },
-] as const;
-
-const mockVideoDuration = 12;
-
-export default function VideoStepScreen({
- backHref,
- nextHref,
- stepLabel,
-}: {
- backHref: string;
- nextHref: string;
- stepLabel: string;
-}) {
- const [isPlaying, setIsPlaying] = useState(false);
- const [watchedSeconds, setWatchedSeconds] = useState(0);
- const isCompleted = watchedSeconds >= mockVideoDuration;
- const progressPercent = Math.min(
- (watchedSeconds / mockVideoDuration) * 100,
- 100,
- );
-
- useEffect(() => {
- if (!isPlaying || isCompleted) {
- return;
- }
-
- const timer = window.setInterval(() => {
- setWatchedSeconds((currentValue) => {
- const nextValue = Math.min(currentValue + 1, mockVideoDuration);
-
- if (nextValue >= mockVideoDuration) {
- setIsPlaying(false);
- }
-
- return nextValue;
- });
- }, 1000);
-
- return () => window.clearInterval(timer);
- }, [isCompleted, isPlaying]);
-
- const handlePlay = () => {
- if (isCompleted) {
- setWatchedSeconds(0);
- }
-
- setIsPlaying((currentValue) => !currentValue || isCompleted);
- };
-
- return (
-
-
-
-
-
-
-
-
-
-
Guided intake
-
- Habib Marriage
-
-
-
-
-
-
-
-
-
-
-
-
Orientation video
-
- Watch this short introduction before continuing
-
-
-
{stepLabel}
-
-
-
- This preview explains the tone of the platform and unlocks the next
- step once the full clip has been watched.
-
-
-
-
-
-
-
-
-
-
-
- 12s walkthrough
-
-
- {watchedSeconds}s / {mockVideoDuration}s
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {metrics.map((metric) => (
-
-
- {metric.value}
-
-
- {metric.label}
-
-
- ))}
-
-
-
-
-
-
-
-
- Watch until the end to unlock the next screen.
-
-
- Your progress bar fills as the preview runs. Replay is available
- after completion.
-
-
-
-
-
-
- Next
-
-
-
- );
-}
diff --git a/src/components/ui/fabric-mobile.tsx b/src/components/ui/fabric-mobile.tsx
deleted file mode 100644
index befd265..0000000
--- a/src/components/ui/fabric-mobile.tsx
+++ /dev/null
@@ -1,214 +0,0 @@
-import Link from "next/link";
-import type {
- ButtonHTMLAttributes,
- ComponentPropsWithoutRef,
- ReactNode,
-} from "react";
-
-function mergeClasses(...classes: Array) {
- return classes.filter(Boolean).join(" ");
-}
-
-export function FabricScreen({
- children,
- className,
- contentClassName,
-}: {
- children: ReactNode;
- className?: string;
- contentClassName?: string;
-}) {
- return (
-
-
-
- );
-}
-
-export function FabricStatusBar() {
- return (
-
- );
-}
-
-export function FabricCard({
- children,
- className,
-}: {
- children: ReactNode;
- className?: string;
-}) {
- return (
- {children}
- );
-}
-
-export function FabricPill({
- children,
- className,
-}: {
- children: ReactNode;
- className?: string;
-}) {
- return (
- {children}
- );
-}
-
-export function FabricProgress({
- className,
- fillClassName,
- value,
-}: {
- className?: string;
- fillClassName?: string;
- value: number;
-}) {
- const clampedValue = Math.max(0, Math.min(value, 100));
-
- return (
-
- );
-}
-
-export function FabricIconButton({
- children,
- className,
- type = "button",
- ...props
-}: ButtonHTMLAttributes & {
- children: ReactNode;
- className?: string;
-}) {
- return (
-
- {children}
-
- );
-}
-
-export function FabricIconLink({
- children,
- className,
- ...props
-}: ComponentPropsWithoutRef & {
- children: ReactNode;
- className?: string;
-}) {
- return (
-
- {children}
-
- );
-}
-
-export function BackIcon() {
- return (
-
-
-
- );
-}
-
-export function HistoryIcon() {
- return (
-
-
-
-
- );
-}
-
-export function PlayIcon() {
- return (
-
-
-
- );
-}
-
-export function CheckIcon() {
- return (
-
-
-
- );
-}
-
-export const fabricInputClass = "fabric-input";
-export const fabricTextareaClass = "fabric-input fabric-textarea";
-export const fabricPrimaryButtonClass = "fabric-primary-button";
-export const fabricSecondaryButtonClass = "fabric-secondary-button";
-export const fabricMutedPanelClass = "fabric-muted-panel";
-export const fabricDisabledLinkClass = "fabric-link-disabled";
diff --git a/src/lib/detailed-questions.ts b/src/lib/detailed-questions.ts
deleted file mode 100644
index 709b166..0000000
--- a/src/lib/detailed-questions.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-export type DetailedQuestionField =
- | {
- description: string;
- id: string;
- label: string;
- placeholder: string;
- type: "number" | "text";
- }
- | {
- description: string;
- id: string;
- label: string;
- type: "date";
- }
- | {
- description: string;
- id: string;
- label: string;
- placeholder: string;
- type: "textarea";
- };
-
-export type DetailedSection = {
- description: string;
- id: string;
- questions: DetailedQuestionField[];
- title: string;
-};
-
-export const detailedSections: DetailedSection[] = [
- {
- description:
- "Add a few practical details about your work, income readiness, and future planning.",
- id: "financial-status",
- questions: [
- {
- description:
- "Enter an approximate monthly amount in your local currency.",
- id: "monthly_income",
- label: "What is your approximate monthly income?",
- placeholder: "e.g. 4500",
- type: "number",
- },
- {
- description:
- "Mention your profession, current work situation, or study path if relevant.",
- id: "work_summary",
- label: "How would you describe your work or financial situation?",
- placeholder:
- "Write a short summary about your work and financial stability",
- type: "textarea",
- },
- {
- description:
- "Pick a realistic date for when you feel prepared to marry.",
- id: "marriage_ready_date",
- label: "By what date do you hope to be financially ready for marriage?",
- type: "date",
- },
- ],
- title: "Financial Status",
- },
- {
- description:
- "Share background that helps frame family expectations, household dynamics, and support.",
- id: "family-background",
- questions: [
- {
- description:
- "Include city, country, or current living arrangement if useful.",
- id: "family_home",
- label: "Where is your family currently based?",
- placeholder: "e.g. Tehran, Iran",
- type: "text",
- },
- {
- description:
- "A short note about parental support, guardian involvement, or family culture is enough.",
- id: "family_expectations",
- label:
- "How would you describe your family environment and expectations?",
- placeholder:
- "Describe your family involvement and expectations around marriage",
- type: "textarea",
- },
- {
- description: "Use the date that matters most for your family process.",
- id: "family_meeting_date",
- label: "When would your family be ready for formal introductions?",
- type: "date",
- },
- ],
- title: "Family Background",
- },
- {
- description:
- "Capture a few core details about routines, values, and the way you want to build your home.",
- id: "personal-practice",
- questions: [
- {
- description:
- "A simple number helps set expectations about schedule and location.",
- id: "weekly_schedule",
- label:
- "How many evenings per week are you usually free for family time?",
- placeholder: "e.g. 4",
- type: "number",
- },
- {
- description:
- "Explain the values, habits, and home atmosphere you hope to maintain.",
- id: "home_values",
- label:
- "What kind of home life and religious practice do you hope to maintain?",
- placeholder:
- "Write about the values and rhythm you want in married life",
- type: "textarea",
- },
- {
- description:
- "Pick a date that reflects when you want to begin active spouse meetings.",
- id: "search_start",
- label: "When would you like to begin serious spouse discussions?",
- type: "date",
- },
- ],
- title: "Personal Practice",
- },
-];
-
-export function getDetailedSection(sectionId: string) {
- return detailedSections.find((section) => section.id === sectionId);
-}
-
-export function getDetailedSectionStorageKey(sectionId: string) {
- return `habib-detailed-section-${sectionId}`;
-}
-
-export function countCompletedDetailedAnswers(
- section: DetailedSection,
- answers: Record,
-) {
- return section.questions.filter((question) => {
- const value = answers[question.id];
- return typeof value === "string" && value.trim().length > 0;
- }).length;
-}
-
-export function getDetailedSectionProgress(
- section: DetailedSection,
- answers: Record,
-) {
- if (section.questions.length === 0) {
- return 0;
- }
-
- return Math.round(
- (countCompletedDetailedAnswers(section, answers) /
- section.questions.length) *
- 100,
- );
-}
-
-export function getDetailedQuestionCount() {
- return detailedSections.reduce(
- (total, section) => total + section.questions.length,
- 0,
- );
-}
diff --git a/src/plugins/add-data-locator.js b/src/plugins/add-data-locator.js
deleted file mode 100644
index c7e7d6d..0000000
--- a/src/plugins/add-data-locator.js
+++ /dev/null
@@ -1,40 +0,0 @@
-module.exports = function addDataLocator({ types: t }) {
- return {
- name: "add-data-locator",
- visitor: {
- JSXOpeningElement(path, state) {
- const filePath = state.file.opts.filename;
-
- if (
- !filePath ||
- filePath.includes("node_modules") ||
- filePath.includes(".next")
- ) {
- return;
- }
-
- const attributeExists = path.node.attributes.some(
- (attribute) =>
- attribute?.type === "JSXAttribute" &&
- attribute.name?.type === "JSXIdentifier" &&
- attribute.name.name === "data-locator",
- );
-
- if (attributeExists) {
- return;
- }
-
- const lineNumber = path.node.loc?.start.line ?? "unknown";
- const columnNumber = path.node.loc?.start.column ?? "unknown";
- const locatorValue = `${filePath}:${lineNumber}:${columnNumber}`;
-
- path.node.attributes.push(
- t.jsxAttribute(
- t.jsxIdentifier("data-locator"),
- t.stringLiteral(locatorValue),
- ),
- );
- },
- },
- };
-};