added subsciption form and created some colors classes

This commit is contained in:
pramod.mahajan 2025-10-26 13:35:06 +05:30
parent 7c5dca1665
commit 11354c2c85
6 changed files with 405 additions and 0 deletions

View File

@ -4,6 +4,29 @@
--bg-border-color :#f8f6f6
}
/* ===========================% Background_Colors %========================================================== */
.bg-light-primary {
background-color: color-mix(in srgb, var(--bs-primary) 10.4%, transparent);
border:var(--bs-primary-border-subtle)
}
.bg-light-secondary {
background-color: color-mix(in srgb, var(--bs-secondary) 10.4%, transparent);
}
.bg-light-danger {
background-color: color-mix(in srgb, var(--bs-danger) 10.4%, transparent);
}
.bg-light-success {
background-color: color-mix(in srgb, var(--bs-success) 10.4%, transparent);
}
.bg-light-info {
background-color: color-mix(in srgb, var(--bs-info) 10.4%, transparent);
}
.bg-light-warning {
background-color: color-mix(in srgb, var(--bs-warning) 10.4%, transparent);
}
.card-header {
padding: 0.5rem var(--bs-card-cap-padding-x);
}

38
src/hooks/usePayment.jsx Normal file
View File

@ -0,0 +1,38 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { PaymentRepository } from "../repositories/PaymentRepository";
export const useMakePayment = (onSuccessCallBack) => {
const client = useQueryClient();
return useMutation({
mutationFn: (payload) => PaymentRepository.makePayment(payload),
onSuccess: (_, varibales) => {
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(
error.message ||
error.response.message ||
"Something went wrong.Please try again later.",
"error"
);
},
});
};
export const useVerifyPayment = () => {
const client = useQueryClient();
return useMutation({
mutationFn: (payload) => PaymentRepository.verifyPayment(payload),
onSuccess: (_, varibales) => {
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(
error.message ||
error.response.message ||
"Something went wrong.Please try again later.",
"error"
);
},
});
};

View File

@ -0,0 +1,27 @@
import { z } from "zod";
export const OrganizationSchema = z.object({
firstName: z.string().min(1, "First Name is required"),
lastName: z.string().min(1, "Last Name is required"),
email: z.string().email("Invalid email address"),
billingAddress: z.string().min(1, "Billing Address is required"),
organizationName: z.string().min(1, "Organization Name is required"),
contactNumber: z
.string()
.min(5, "Contact Number is too short")
.regex(
/^[+]?[(]?[0-9]{1,4}[)]?[-\s./0-9]*$/,
"Invalid phone number format"
),
onBoardingDate: z.coerce
.date()
.refine((d) => !Number.isNaN(d.getTime()), { message: "Invalid date" }),
organizationSize: z.string().min(1, "Organization Size is required"),
industryId: z.string().uuid("Industry is required"),
reference: z.string().optional(),
});
export const OrganizationDefaultValue = {
}

View File

@ -0,0 +1,308 @@
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { OrganizationDefaultValue, OrganizationSchema } from "./HomeSchema";
import { zodResolver } from "@hookform/resolvers/zod";
import Label from "../../components/common/Label";
import { orgSize, reference } from "../../utils/constants";
import DatePicker from "../../components/common/DatePicker";
import { useIndustries } from "../../hooks/useTenant";
const MakeSubscription = () => {
const [selectedPlan, setSelectedPlan] = useState("basic");
const handleChange = (e) => {
const value = e.target.value;
setSelectedPlan(value);
};
const { data, isError, isLoading: industryLoading } = useIndustries();
const options = [
{
id: 1,
title: "Basic",
value: "basic", // Added value
price: "Free",
description: "Get 1 project with 1 team member.",
},
{
id: 2,
title: "Pro",
value: "pro", // Added value
price: "$10/mo",
description: "Up to 10 projects and 5 team members.",
},
{
id: 3,
title: "Enterprise",
value: "enterprise", // Added value
price: "$30/mo",
description: "Unlimited projects and team members.",
},
];
const {
register,
handleSubmit,
control,
formState: { errors },
reset,
} = useForm({
resolver: zodResolver(OrganizationSchema),
defaultValues: OrganizationDefaultValue,
});
const onSubmit = (data) => {
console.log("Form Submitted:", data);
alert("Form submitted successfully!");
reset();
};
return (
<div className="container-fluid bg-light-secondary min-vh-100">
<div className="row g-3">
<div className="col-12 col-md-6">
<div className="row px-4">
<div className="text-start ">
{/* <h4 className="mb-3 text-primary">Organization Onboarding Form</h4> */}
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row">
<div className="col-sm-6 mb-3">
<Label htmlFor="firstName" required>
First Name
</Label>
<input
id="firstName"
type="text"
className={`form-control form-control-sm`}
{...register("firstName")}
/>
{errors.firstName && (
<div className="danger-text">
{errors.firstName.message}
</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="lastName" required>
Last Name
</Label>
<input
id="lastName"
type="text"
className={`form-control form-control-sm `}
{...register("lastName")}
/>
{errors.lastName && (
<div className="danger-text">
{errors.lastName.message}
</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="email" required>
Email
</Label>
<input
id="email"
type="email"
className={`form-control form-control-sm `}
{...register("email")}
/>
{errors.email && (
<div className="danger-text">{errors.email.message}</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="contactNumber" required>
Contact Number
</Label>
<input
id="contactNumber"
type="text"
className={`form-control form-control-sm `}
{...register("contactNumber")}
inputMode="tel"
placeholder="+91 9876543210"
/>
{errors.contactNumber && (
<div className="danger-text">
{errors.contactNumber.message}
</div>
)}
</div>
<div className="col-12 mb-3">
<Label htmlFor="billingAddress" required>
Billing Address
</Label>
<textarea
id="billingAddress"
className={`form-control `}
{...register("billingAddress")}
rows={3}
/>
{errors.billingAddress && (
<div className="danger-text">
{errors.billingAddress.message}
</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="organizationName" required>
Organization Name
</Label>
<input
id="organizationName"
className={`form-control form-control-sm `}
{...register("organizationName")}
/>
{errors.organizationName && (
<div className="danger-text">
{errors.organizationName.message}
</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="onBoardingDate" required>
Onboarding Date
</Label>
<DatePicker
name="onBoardingDate"
control={control}
placeholder="DD-MM-YYYY"
maxDate={new Date()}
/>
{errors.onBoardingDate && (
<div className="invalid-feedback">
{errors.onBoardingDate.message}
</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="organizationSize" required>
Organization Size
</Label>
<select
id="organizationSize"
className="form-select shadow-none border py-1 px-2"
style={{ fontSize: "0.875rem" }} // Bootstrap's small text size
{...register("organizationSize", {
required: "Organization size is required",
})}
>
{orgSize.map((org) => (
<option key={org.val} value={org.val}>
{org.name}
</option>
))}
</select>
{errors.organizationSize && (
<div className="danger-text">
{errors.organizationSize.message}
</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="industryId" required>
Industry
</Label>
<select
id="industryId"
className="form-select shadow-none border py-1 px-2 small"
{...register("industryId")}
>
{industryLoading ? (
<option value="">Loading...</option>
) : (
data?.map((indu) => (
<option key={indu.id} value={indu.id}>
{indu.name}
</option>
))
)}
</select>
{errors.industryId && (
<div className="danger-text">
{errors.industryId.message}
</div>
)}
</div>
<div className="col-sm-6 mb-3">
<Label htmlFor="reference" required>
Reference
</Label>
<select
id="reference"
className="form-select shadow-none border py-1 px-2 small"
{...register("reference")}
>
{reference.map((org) => (
<option key={org.val} value={org.val}>
{org.name}
</option>
))}
</select>
{errors.reference && (
<div className="danger-text">
{errors.reference.message}
</div>
)}
</div>
</div>
<div className="text-end mb-3">
<button
type="submit"
className="btn btn-label-primary d-flex align-items-center me-2"
>
<span className="me-1">Next</span>
<i className="bx bx-chevron-right"></i>
</button>
</div>
</form>
</div>
</div>
</div>
<div className="col-12 col-md-6 ">
<div className="row">
{options.map((opt) => (
<div key={opt.id} className="col-md-4 mb-md-3 mb-2">
<div className={`form-check custom-option custom-option-basic text-start w-100 bg-light-primary ${selectedPlan === opt.value ? "border border-primary shadow-md":""}`}>
<label
className="form-check-label custom-option-content w-100"
htmlFor={`customRadioTemp${opt.id}`}
>
<input
name="customRadioTemp"
className="form-check-input"
type="radio"
value={opt.value}
id={`customRadioTemp${opt.id}`}
checked={selectedPlan === opt.value}
onChange={handleChange}
/>
<span className="custom-option-header d-flex justify-content-between align-items-center">
<span className="h6 mb-0">{opt.title}</span>
<span>{opt.price}</span>
</span>
<span className="custom-option-body d-block mt-1">
<small>{opt.description}</small>
</span>
</label>
</div>
</div>
))}
</div>
</div>
</div>
</div>
);
};
export default MakeSubscription;

View File

@ -0,0 +1,7 @@
import { api } from "../utils/axiosClient";
export const PaymentRepository = {
makePayment: () => api.post(`/api/Payment/create-order`),
verifyPayment: () => api.post(`/api/Payment/verify-payment`),
};

View File

@ -54,6 +54,7 @@ import DailyProgrssReport from "../pages/DailyProgressReport/DailyProgrssReport"
import ProjectPage from "../pages/project/ProjectPage";
import { ComingSoonPage } from "../pages/Misc/ComingSoonPage";
import CollectionPage from "../pages/collections/CollectionPage";
import MakeSubscription from "../pages/Home/MakeSubscription";
const router = createBrowserRouter(
[
{
@ -73,6 +74,7 @@ const router = createBrowserRouter(
],
},
{ path: "/auth/switch/org", element: <TenantSelectionPage /> },
{ path: "/request", element: <MakeSubscription /> },
{
element: <ProtectedRoute />,
errorElement: <ErrorPage />,