Merge pull request 'feat: update row actions to include payment functionality and add corresponding icon' (#74) from dev into production

Reviewed-on: #74
This commit was merged in pull request #74.
This commit is contained in:
2026-10-19 22:13:07 +05:30
5 changed files with 69 additions and 39 deletions
@@ -160,7 +160,7 @@ export function OrdersClient() {
visible: (target) => target.status === 'Printed', visible: (target) => target.status === 'Printed',
}, },
{ {
action: 'edit', action: 'pay',
label: 'Record payment', label: 'Record payment',
onClick: handleRecordPayment, onClick: handleRecordPayment,
visible: (target) => target.paymentStatus !== 'Paid', visible: (target) => target.paymentStatus !== 'Paid',
+42
View File
@@ -133,4 +133,46 @@
.react-grid-item > .react-resizable-handle::after { .react-grid-item > .react-resizable-handle::after {
border-right-color: var(--muted-foreground); border-right-color: var(--muted-foreground);
border-bottom-color: var(--muted-foreground); border-bottom-color: var(--muted-foreground);
}
/* Top loading strip: thin bar sliding across with a neon-red leading dot. */
.top-loader {
pointer-events: none;
position: absolute;
left: 0;
right: 0;
top: 0;
height: 2px;
overflow: visible;
z-index: 50;
}
.top-loader__bar {
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 60, 90, 0.35) 55%,
rgba(255, 90, 120, 0.9) 90%,
#ff2e63 100%
);
transform: translateX(-100%);
animation: top-loader-slide 1.2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.top-loader__head {
position: absolute;
right: -4px;
top: -2px;
height: 6px;
width: 10px;
border-radius: 9999px;
background: #ff6b8a;
box-shadow:
0 0 6px 1px #ff2e63,
0 0 14px 2px rgba(255, 46, 99, 0.75),
0 0 24px 4px rgba(255, 46, 99, 0.35);
}
@keyframes top-loader-slide {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
} }
@@ -13,7 +13,8 @@ export type RowActionKind =
| 'cancel' | 'cancel'
| 'print' | 'print'
| 'finalize' | 'finalize'
| 'generate'; | 'generate'
| 'pay';
export interface BadgeConfig { export interface BadgeConfig {
colorMap?: Record<string, string>; colorMap?: Record<string, string>;
@@ -3,6 +3,7 @@
import * as React from "react" import * as React from "react"
import { HugeiconsIcon } from "@hugeicons/react" import { HugeiconsIcon } from "@hugeicons/react"
import { import {
AddMoneyCircleIcon,
Cancel01Icon, Cancel01Icon,
CheckmarkCircle02Icon, CheckmarkCircle02Icon,
CheckmarkSquare01Icon, CheckmarkSquare01Icon,
@@ -24,6 +25,7 @@ const ACTION_ICONS = {
edit: PencilEdit01Icon, edit: PencilEdit01Icon,
finalize: CheckmarkSquare01Icon, finalize: CheckmarkSquare01Icon,
generate: CheckmarkSquare01Icon, generate: CheckmarkSquare01Icon,
pay: AddMoneyCircleIcon,
print: PrinterIcon, print: PrinterIcon,
view: EyeIcon, view: EyeIcon,
} as const } as const
@@ -1,44 +1,29 @@
'use client' 'use client'
export function TopLoader({ active }: { active: boolean }) { import { useEffect, useState } from 'react'
if (!active) return null
/**
* Thin top strip shown while `active` is true. Stays visible for at least
* `minDurationMs` so quick fetches don't produce an invisible flicker.
*/
export function TopLoader({ active, minDurationMs = 350 }: { active: boolean; minDurationMs?: number }) {
const [visible, setVisible] = useState(active)
useEffect(() => {
if (active) {
setVisible(true)
return
}
const t = setTimeout(() => setVisible(false), minDurationMs)
return () => clearTimeout(t)
}, [active, minDurationMs])
if (!visible) return null
return ( return (
<div className="pointer-events-none absolute inset-x-0 top-0 h-[2px] overflow-hidden"> <div className="top-loader">
<div className="top-loader-bar"> <div className="top-loader__bar">
<span className="top-loader-head" /> <span className="top-loader__head" />
</div> </div>
<style jsx>{`
.top-loader-bar {
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 60, 90, 0.45) 50%,
rgba(255, 90, 120, 0.9) 85%,
#ff2e63 100%
);
transform: translateX(-100%);
animation: top-loader-slide 1.1s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.top-loader-head {
position: absolute;
right: 0;
top: -2px;
height: 6px;
width: 10px;
border-radius: 9999px;
background: #ff6b8a;
box-shadow:
0 0 6px 1px #ff2e63,
0 0 14px 2px rgba(255, 46, 99, 0.75),
0 0 24px 4px rgba(255, 46, 99, 0.35);
}
@keyframes top-loader-slide {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
`}</style>
</div> </div>
) )
} }