mirror of
				https://github.com/i701/sarlink-portal.git
				synced 2025-11-04 12:36:59 +00:00 
			
		
		
		
	Add payment processing and device management features
- Introduced createPayment action for handling payment creation. - Added PaymentsTable component for displaying payment records with pagination. - Implemented new PaymentPage for viewing individual payment details and associated devices. - Refactored DeviceCartDrawer to integrate payment creation and device selection. - Enhanced DevicesToPay component to display devices based on payment status. - Updated PriceCalculator component for better user input handling. - Introduced NumberInput component for consistent number input across forms. - Modified Prisma schema to include new fields for payments and devices. - Improved overall user experience with responsive design adjustments and new UI elements.
This commit is contained in:
		@@ -1,125 +1,8 @@
 | 
			
		||||
"use client";
 | 
			
		||||
import {
 | 
			
		||||
  discountPercentageAtom,
 | 
			
		||||
  formulaResultAtom,
 | 
			
		||||
  initialPriceAtom,
 | 
			
		||||
  numberOfDaysAtom,
 | 
			
		||||
  numberOfDevicesAtom,
 | 
			
		||||
} from "@/lib/atoms";
 | 
			
		||||
import { useAtom } from "jotai";
 | 
			
		||||
import { Minus, Plus } from "lucide-react";
 | 
			
		||||
import { useEffect } from "react";
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  Group,
 | 
			
		||||
  Input,
 | 
			
		||||
  Label,
 | 
			
		||||
  NumberField,
 | 
			
		||||
} from "react-aria-components";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default function PriceCalculator() {
 | 
			
		||||
  const [initialPrice, setInitialPrice] = useAtom(initialPriceAtom);
 | 
			
		||||
  const [discountPercentage, setDiscountPercentage] = useAtom(
 | 
			
		||||
    discountPercentageAtom,
 | 
			
		||||
  );
 | 
			
		||||
  const [numberOfDevices, setNumberOfDevices] = useAtom(numberOfDevicesAtom);
 | 
			
		||||
  const [numberOfDays, setNumberOfDays] = useAtom(numberOfDaysAtom);
 | 
			
		||||
  const [formulaResult, setFormulaResult] = useAtom(formulaResultAtom);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const basePrice = initialPrice + (numberOfDevices - 1) * discountPercentage;
 | 
			
		||||
    setFormulaResult(
 | 
			
		||||
      `Price for ${numberOfDevices} device(s) over ${numberOfDays} day(s): MVR ${basePrice.toFixed(2)}`,
 | 
			
		||||
    );
 | 
			
		||||
  }, [
 | 
			
		||||
    initialPrice,
 | 
			
		||||
    discountPercentage,
 | 
			
		||||
    numberOfDevices,
 | 
			
		||||
    numberOfDays,
 | 
			
		||||
    setFormulaResult,
 | 
			
		||||
  ]);
 | 
			
		||||
import PriceCalculator from '@/components/price-calculator'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
 | 
			
		||||
export default function Pricing() {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="border p-2 rounded-xl">
 | 
			
		||||
      <div className="flex flex-col justify-between items-start text-gray-500 title-bg p-2 mb-4">
 | 
			
		||||
        <h3 className="text-2xl font-semibold">Price Calculator</h3>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
 | 
			
		||||
        {/* Initial Price Input */}
 | 
			
		||||
        <NumberInput
 | 
			
		||||
          label="Initial Price"
 | 
			
		||||
          value={initialPrice}
 | 
			
		||||
          onChange={(value) => setInitialPrice(value)}
 | 
			
		||||
        />
 | 
			
		||||
        {/* Number of Devices Input */}
 | 
			
		||||
        <NumberInput
 | 
			
		||||
          label="Number of Devices"
 | 
			
		||||
          value={numberOfDevices}
 | 
			
		||||
          onChange={(value) => setNumberOfDevices(value)}
 | 
			
		||||
        />
 | 
			
		||||
        {/* Number of Days Input */}
 | 
			
		||||
        <NumberInput
 | 
			
		||||
          label="Number of Days"
 | 
			
		||||
          value={numberOfDays}
 | 
			
		||||
          onChange={(value) => setNumberOfDays(value)}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        {/* Discount Percentage Input */}
 | 
			
		||||
        <NumberInput
 | 
			
		||||
          label="Discount Percentage"
 | 
			
		||||
          value={discountPercentage}
 | 
			
		||||
          onChange={(value) => setDiscountPercentage(value)}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div className="mt-4">
 | 
			
		||||
        <div className="title-bg relative rounded-lg border border-input shadow-sm shadow-black/5 transition-shadow focus-within:border-ring focus-within:outline-none focus-within:ring-[3px] focus-within:ring-ring/20 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 [&:has(input:is(:disabled))_*]:pointer-events-none">
 | 
			
		||||
          <label
 | 
			
		||||
            htmlFor=""
 | 
			
		||||
            className="block px-3 pt-2 text-md font-medium text-foreground"
 | 
			
		||||
          >
 | 
			
		||||
            Total
 | 
			
		||||
          </label>
 | 
			
		||||
          <input
 | 
			
		||||
            className="flex font-mono font-semibold h-10 w-full bg-transparent px-3 pb-2 text-sm text-foreground placeholder:text-muted-foreground/70 focus-visible:outline-none"
 | 
			
		||||
            value={formulaResult}
 | 
			
		||||
            readOnly
 | 
			
		||||
            placeholder={"Result"}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dependencies: pnpm install lucide-react react-aria-components
 | 
			
		||||
 | 
			
		||||
function NumberInput({
 | 
			
		||||
  label,
 | 
			
		||||
  value,
 | 
			
		||||
  onChange,
 | 
			
		||||
}: { label: string; value: number; onChange: (value: number) => void }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <NumberField value={value} minValue={0} onChange={onChange}>
 | 
			
		||||
      <div className="space-y-2">
 | 
			
		||||
        <Label className="text-sm font-medium text-foreground">{label}</Label>
 | 
			
		||||
        <Group className="relative inline-flex h-9 w-full items-center overflow-hidden whitespace-nowrap rounded-lg border border-input text-sm shadow-sm shadow-black/5 transition-shadow data-[focus-within]:border-ring data-[disabled]:opacity-50 data-[focus-within]:outline-none data-[focus-within]:ring-[3px] data-[focus-within]:ring-ring/20">
 | 
			
		||||
          <Button
 | 
			
		||||
            slot="decrement"
 | 
			
		||||
            className="-ms-px flex aspect-square h-[inherit] items-center justify-center rounded-s-lg border border-input bg-background text-sm text-muted-foreground/80 transition-shadow hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
 | 
			
		||||
          >
 | 
			
		||||
            <Minus size={16} strokeWidth={2} aria-hidden="true" />
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Input className="w-full grow bg-background px-3 py-2 text-center tabular-nums text-foreground focus:outline-none" />
 | 
			
		||||
          <Button
 | 
			
		||||
            slot="increment"
 | 
			
		||||
            className="-me-px flex aspect-square h-[inherit] items-center justify-center rounded-e-lg border border-input bg-background text-sm text-muted-foreground/80 transition-shadow hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50"
 | 
			
		||||
          >
 | 
			
		||||
            <Plus size={16} strokeWidth={2} aria-hidden="true" />
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Group>
 | 
			
		||||
      </div>
 | 
			
		||||
    </NumberField>
 | 
			
		||||
  );
 | 
			
		||||
    <PriceCalculator />
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user