Adding dynamic input system, and some minor improvements in types.ts, registry.ts and test.ts

This commit is contained in:
2026-03-30 15:11:22 +05:30
parent 73e6bb7dd8
commit bb746ee8f6
11 changed files with 149 additions and 31 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 168 KiB

View File

@@ -1,12 +1,29 @@
import InputComponent from "@/app/utils/Input";
import Input from "@/app/utils/Input";
import { CalculatorRegistry } from "@/app/utils/calculators/registry";
export default async function Calculator({params} : {params:Promise<{section:string, id:string}>}){
const calcData : {section:string, id:string} = await params;
return(
const calculator = CalculatorRegistry[calcData.section].calculators.find(e => e.id === calcData.id);
console.log(calculator);
return calculator ? (
<>
<h1>Section:- {calcData.section}</h1>
<h1>Calculator ID:- {calcData.id}</h1>
<Input/>
<div className="mb-6 mt-10">
<h1 className="text-2xl font-semibold">
{calculator?.name}
</h1>
<p className="text-gray-500 mt-1 max-w-xl">
{calculator?.desc}
</p>
<h1 className="text-1xl font-semibold">
Unit of the value:- {calculator?.unit}
</h1>
</div>
{calculator.inputs.map(e => {
return(
<InputComponent {...e}/>
);
})}
</>
)
) : null;
}

View File

@@ -1,5 +1,5 @@
import LayoutClient from "./layoutClient";
import { Roboto } from "next/font/google";
export default function CalculatorsLayout({children} : {children:React.ReactNode}){
return(
<>

View File

@@ -2,11 +2,12 @@
import { useState } from "react";
import Sidemenu from "../utils/Sidemenu";
import Navbar from "../utils/Navbar";
import { Roboto } from "next/font/google";
const font = Roboto({});
export default function LayoutClient({children} : {children:React.ReactNode}){
const [navbar, setNavbar] = useState<boolean>(false);
return(
<div className="grid grid-cols-[300px_1fr] grid-rows-[60px_1fr_40px] overflow-hidden calculators-container">
<div className={`grid grid-cols-[300px_1fr] grid-rows-[60px_1fr_40px] overflow-hidden calculators-container ${font.className}`}>
<Navbar navbarToggle={setNavbar}/>
<Sidemenu isNavOpen={navbar}/>
<div className="content">

View File

@@ -1,11 +1,64 @@
export default function Input(){
"use client"
import { useState } from "react";
import { Input } from "./calculators/types";
export default function InputComponent({id, type, inputOptions, min, max, required, defaultUnit, unitOptions, name, placeholder} : Input){
const [showError, setErrorStatus] = useState(false);
const [error, setError] = useState<string>();
const [option, setOption] = useState<string>("");
console.log(id);
if(type === "number"){
console.log(min);
return(
<>
<fieldset className="fieldset">
<legend className="fieldset-legend text-black">Page title</legend>
<input type="text" className="input bg-white" placeholder="My awesome page" />
<p className="label">You can edit page title later on from settings</p>
<legend className="fieldset-legend text-black">{name}</legend>
<input type="number"
id={id}
className="input bg-white"
placeholder={placeholder}
min={min}
max={max}
required={required}
onChange={(e) => {
if(e.target.value === ""){
return;
}
const val = Number(e.target.value);
if(min !== undefined && val < min){
setError(`Value must be greater than or equal to ${min}`)
setErrorStatus(true);
}else if(max !== undefined && val > max){
setError(`Value must be smaller than or equal to ${max}`);
setErrorStatus(true);
}else{
setError("");
setErrorStatus(false);
}
}}
/>
{showError ? <p className="label text-red-400">{error}</p> : null}
</fieldset>
</>
);
} else if(type === "select"){
return(
<fieldset className="fieldset">
<legend className="fieldset-legend text-black">{name}</legend>
<select value={option}
className="select bg-white"
onChange={(e) => {
setOption(e.target.value);
}}>
<option value="" disabled selected hidden>{placeholder}</option>
{inputOptions?.map(e => {
return(
<option key={e.label} value={e.value}>{e.value}</option>
)
})}
</select>
{/* <span className="label text-red-400">Optional</span> */}
</fieldset>
)
}
}

View File

@@ -1,12 +1,12 @@
"use client"
import Link from "next/link";
type Props = {
navbarToggle: React.Dispatch<React.SetStateAction<boolean>>;
};
export default function Navbar({navbarToggle}: Props){
return(
<div className="navbar bg-base-100 shadow-sm z-50">
<div className="navbar h-14 bg-base-100 shadow-sm z-50">
<div className="navbar-start">
<div className="dropdown">
<button onClick={
@@ -21,11 +21,13 @@ export default function Navbar({navbarToggle}: Props){
</div>
</div>
<div className="navbar-center">
<a href="/" className="flex items-center gap-2 leading-none translate-y-[10px]">
<img src="/calcforcardiac_logo.png" className="w-32 h-32 object-contain" />
<span className="text-lg font-semibold flex items-center relative top-[-10px]">CalcForCardiac</span>
</a>
<div className="navbar-center flex items-center justify-center">
<Link href="/" className="flex items-center gap-2 w-fit">
<img src="/calcforcardiac_logo.png" className="h-9 md:h-10 w-auto object-contain" />
<span className="text-lg font-semibold">
CalcForCardiac
</span>
</Link>
</div>
<div className="navbar-end">
<button className="btn btn-ghost btn-circle">

View File

@@ -1,9 +1,11 @@
import { Calculator, Input, Interpretation } from "../types";
import { Calculator, Input, Interpretation, Values } from "../types";
const parameters:Input[] = [
{
id:"gender",
type:"radio",
name:"Gender",
placeholder:"Enter your gender",
type:"select",
inputOptions:[
{
label:"Male",
@@ -19,6 +21,8 @@ const parameters:Input[] = [
{
id:"age",
type:"number",
name:"Age",
placeholder:"Enter your age",
min:18,
max:95,
required:true
@@ -26,6 +30,8 @@ const parameters:Input[] = [
{
id:"creatinine",
type:"number",
name:"Creatinine",
placeholder:"Enter your serum creatinine values",
min:0.2,
max:20,
required:true
@@ -33,11 +39,18 @@ const parameters:Input[] = [
{
id:"weight",
type:"number",
min:0,
name:"Weight",
placeholder:"Enter your weight in kilograms",
min:10,
max:100 ,
required:true
}
];
function calc_func(gender:"male" | "female", age:number, creatinine:number, weight:number):number{
function calc_func(values : Values):number{
const gender = values.gender as "male" | "female";
const age = values.age as number;
const weight = values.weight as number;
const creatinine = values.creatinine as number;
return gender === "male"
? ((140 - age) * weight)/(72 * creatinine)
: (((140 - age) * weight)/(72 * creatinine)) * 0.85;

View File

@@ -0,0 +1,10 @@
import { CalculatorRegistry } from "../registry";
const calculator = CalculatorRegistry.miscellaneous.calculators[0].calc_func;
console.log(calculator(
{
gender:"male",
age:43,
creatinine:5,
weight:60
}
));

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CalculatorRegistry = void 0;
var cockcroft_gault_eq_1 = require("./miscellaneous/cockcroft-gault-eq");
exports.CalculatorRegistry = {
miscellaneous: {
id: "miscellaneous",
displayName: "Miscellaneous",
textColor: "#ccc",
svg: "",
calculators: [
cockcroft_gault_eq_1.cockcroftGault
]
}
};

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -5,15 +5,18 @@ export interface Calculator {
unit?:string,
inputs:Input[],
calc_func:Function,
interpret_func:Function
interpret_func:Function,
customComponent?:React.FC
}
interface inputOptions {
label:string,
value:string | number | Date;
value:string | number ;
}
export interface Input{
id:string,
type: "number" | "text" | "radio",
name:string,
placeholder:string,
type: "number" | "text" | "select",
inputOptions?:inputOptions[],
min?:number,
max?:number,
@@ -35,3 +38,5 @@ export interface Section {
svg:string,
calculators:Calculator[]
}
export type Values = Record<string, string | number >;