From c0fd8671e4630220ca8507815237ef92db2aa75b Mon Sep 17 00:00:00 2001
From: shafi54 <108669266+shafi-aviz@users.noreply.github.com>
Date: Sat, 23 May 2026 14:47:12 +0530
Subject: [PATCH] common components
---
apps/pharmanager/package.json | 1 +
apps/pharmanager/src/components/ui/Button.tsx | 53 +++++++++++++++++++
apps/pharmanager/src/components/ui/Input.tsx | 39 ++++++++++++++
.../src/components/ui/Textarea.tsx | 36 +++++++++++++
apps/pharmanager/src/components/ui/index.ts | 3 ++
.../src/routes/distributors/$id.tsx | 18 +++----
.../src/routes/distributors/add.tsx | 23 ++++----
.../src/routes/distributors/index.tsx | 19 +++----
apps/pharmanager/src/routes/storage/$id.tsx | 18 +++----
apps/pharmanager/src/routes/storage/add.tsx | 52 +++++++++---------
apps/pharmanager/src/routes/storage/index.tsx | 19 +++----
11 files changed, 203 insertions(+), 78 deletions(-)
create mode 100644 apps/pharmanager/src/components/ui/Button.tsx
create mode 100644 apps/pharmanager/src/components/ui/Input.tsx
create mode 100644 apps/pharmanager/src/components/ui/Textarea.tsx
create mode 100644 apps/pharmanager/src/components/ui/index.ts
diff --git a/apps/pharmanager/package.json b/apps/pharmanager/package.json
index 83f021e..fb2cd02 100644
--- a/apps/pharmanager/package.json
+++ b/apps/pharmanager/package.json
@@ -22,6 +22,7 @@
"@tanstack/react-router-devtools": "latest",
"@tanstack/react-table": "^8.21.3",
"@tanstack/router-plugin": "^1.132.0",
+ "class-variance-authority": "^0.7.1",
"lucide-react": "^0.545.0",
"react": "19.1.0",
"react-dom": "19.1.0",
diff --git a/apps/pharmanager/src/components/ui/Button.tsx b/apps/pharmanager/src/components/ui/Button.tsx
new file mode 100644
index 0000000..1477ad7
--- /dev/null
+++ b/apps/pharmanager/src/components/ui/Button.tsx
@@ -0,0 +1,53 @@
+import { forwardRef, type ComponentPropsWithoutRef } from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+import { cn } from "#/lib/cn";
+
+export const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-1.5 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none",
+ {
+ variants: {
+ variant: {
+ primary: "bg-blue-600 text-white hover:bg-blue-700",
+ danger: "bg-red-600 text-white hover:bg-red-700",
+ outline:
+ "border border-slate-200 bg-white text-slate-700 hover:bg-slate-50",
+ ghost: "text-slate-500 hover:bg-slate-100 hover:text-slate-900",
+ "ghost-blue":
+ "text-slate-500 hover:text-blue-600 hover:bg-blue-50",
+ "ghost-red":
+ "text-slate-500 hover:text-red-600 hover:bg-red-50",
+ "ghost-dark":
+ "text-slate-400 hover:bg-slate-800 hover:text-white",
+ },
+ size: {
+ default: "px-4 py-2",
+ sm: "px-3 py-1.5 text-xs",
+ lg: "px-6 py-2.5",
+ icon: "w-8 h-8 p-0",
+ "icon-sm": "w-4 h-4 p-0 rounded-full",
+ },
+ },
+ defaultVariants: {
+ variant: "primary",
+ size: "default",
+ },
+ },
+);
+
+export interface ButtonProps
+ extends ComponentPropsWithoutRef<"button">,
+ VariantProps {}
+
+export const Button = forwardRef(
+ ({ className, variant, size, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+
+Button.displayName = "Button";
diff --git a/apps/pharmanager/src/components/ui/Input.tsx b/apps/pharmanager/src/components/ui/Input.tsx
new file mode 100644
index 0000000..1d6ad2f
--- /dev/null
+++ b/apps/pharmanager/src/components/ui/Input.tsx
@@ -0,0 +1,39 @@
+import { forwardRef, type ComponentPropsWithoutRef } from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+import { cn } from "#/lib/cn";
+
+export const inputVariants = cva(
+ "px-3.5 py-2.5 border rounded-md text-sm text-slate-900 bg-white transition-colors focus:outline-none focus:ring-[3px] focus:ring-blue-100 w-full",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-slate-200 focus:border-blue-600",
+ error: "border-red-600",
+ transparent: "border-none bg-transparent",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+export interface InputProps
+ extends ComponentPropsWithoutRef<"input">,
+ VariantProps {}
+
+export const Input = forwardRef(
+ ({ className, variant, type, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+
+Input.displayName = "Input";
diff --git a/apps/pharmanager/src/components/ui/Textarea.tsx b/apps/pharmanager/src/components/ui/Textarea.tsx
new file mode 100644
index 0000000..4a1160a
--- /dev/null
+++ b/apps/pharmanager/src/components/ui/Textarea.tsx
@@ -0,0 +1,36 @@
+import { forwardRef, type ComponentPropsWithoutRef } from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+import { cn } from "#/lib/cn";
+
+export const textareaVariants = cva(
+ "w-full px-3.5 py-2.5 border rounded-md text-sm text-slate-900 bg-white resize-y min-h-[80px] transition-colors focus:outline-none focus:ring-[3px] focus:ring-blue-100",
+ {
+ variants: {
+ variant: {
+ default: "border-slate-200 focus:border-blue-600",
+ error: "border-red-600",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+export interface TextareaProps
+ extends ComponentPropsWithoutRef<"textarea">,
+ VariantProps {}
+
+export const Textarea = forwardRef(
+ ({ className, variant, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+
+Textarea.displayName = "Textarea";
diff --git a/apps/pharmanager/src/components/ui/index.ts b/apps/pharmanager/src/components/ui/index.ts
new file mode 100644
index 0000000..956dc57
--- /dev/null
+++ b/apps/pharmanager/src/components/ui/index.ts
@@ -0,0 +1,3 @@
+export { Button, type ButtonProps, buttonVariants } from "./Button";
+export { Input, type InputProps, inputVariants } from "./Input";
+export { Textarea, type TextareaProps, textareaVariants } from "./Textarea";
diff --git a/apps/pharmanager/src/routes/distributors/$id.tsx b/apps/pharmanager/src/routes/distributors/$id.tsx
index 7f3e0bb..6dbd132 100644
--- a/apps/pharmanager/src/routes/distributors/$id.tsx
+++ b/apps/pharmanager/src/routes/distributors/$id.tsx
@@ -1,5 +1,6 @@
import { createFileRoute, Link } from "@tanstack/react-router";
import { ArrowLeft, Pencil, Trash2, Truck } from "lucide-react";
+import { Button, buttonVariants } from "#/components/ui";
import {
useGetDistributorById,
useRemoveDistributor,
@@ -56,7 +57,7 @@ function DistributorDetailsPage() {
Back to Distributors
@@ -124,21 +125,14 @@ function DistributorDetailsPage() {
-
diff --git a/apps/pharmanager/src/routes/distributors/add.tsx b/apps/pharmanager/src/routes/distributors/add.tsx
index a6a6a6d..1f482fb 100644
--- a/apps/pharmanager/src/routes/distributors/add.tsx
+++ b/apps/pharmanager/src/routes/distributors/add.tsx
@@ -4,6 +4,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useCreateDistributor, trpc } from "shared-react";
+import { Button, Input, Textarea, buttonVariants } from "#/components/ui";
const formSchema = z.object({
agency: z.string().min(1, "Agency name is required"),
@@ -81,11 +82,11 @@ function AddDistributorPage() {
Agency Name{" "}
*
-
{errors.agency && (
@@ -99,11 +100,11 @@ function AddDistributorPage() {
Contact Person{" "}
*
-
{errors.contact && (
@@ -117,8 +118,9 @@ function AddDistributorPage() {
Contact Mobile{" "}
*
- {
e.target.value = e.target.value
@@ -127,7 +129,6 @@ function AddDistributorPage() {
},
})}
placeholder="e.g. 9876500001"
- className={`w-full px-3.5 py-2.5 border rounded-md text-sm text-slate-900 bg-white transition-colors focus:outline-none focus:ring-[3px] focus:ring-blue-100 ${errors.mobile ? "border-red-600" : "border-slate-200 focus:border-blue-600"}`}
/>
{errors.mobile && (
@@ -140,13 +141,12 @@ function AddDistributorPage() {
-
Back to Storage
@@ -91,21 +92,14 @@ function StorageDetailsPage() {
)}
-
+
Edit
-
-
+
+
Delete
-
+
diff --git a/apps/pharmanager/src/routes/storage/add.tsx b/apps/pharmanager/src/routes/storage/add.tsx
index c098b7f..16d44bc 100644
--- a/apps/pharmanager/src/routes/storage/add.tsx
+++ b/apps/pharmanager/src/routes/storage/add.tsx
@@ -5,6 +5,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useCreateStorage, trpc } from "shared-react";
+import { Button, Input, Textarea, buttonVariants } from "#/components/ui";
const formSchema = z.object({
name: z.string().min(1, "Rack name is required"),
@@ -132,11 +133,11 @@ function AddStoragePage() {
-
{errors.name && (
@@ -149,13 +150,12 @@ function AddStoragePage() {
-