Merge pull request #24 from Hestia-Homes/stefportfoliosettings

fixed issues with number input on firefox,
This commit is contained in:
KhalimCK 2024-10-24 15:45:54 +01:00 committed by GitHub
commit 5f2cc6b1b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 264 additions and 18 deletions

View file

@ -5,6 +5,57 @@ import { PortfolioSettingsType } from "../../utils";
import { Button } from "@/app/shadcn_components/ui/button";
import { Input } from "@/app/shadcn_components/ui/input";
import { useRouter } from "next/navigation";
import { handleNumericKeyDown } from "@/app/utils";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/app/shadcn_components/ui/select";
import {
Dialog,
DialogContent,
DialogTitle,
DialogFooter,
} from "@/app/shadcn_components/ui/dialog";
import { PortfolioStatus as PortfolioStatusOptions } from "@/app/db/schema/portfolio";
import { PortfolioGoal as PortfolioGoalOptions } from "@/app/db/schema/portfolio";
// dropdown selection component for both goal and status
export function SettingsDropdown({
startingValue,
options,
setOption,
}: {
startingValue: string;
options: string[];
setOption: (option: string) => void;
}) {
function handleValueChange(newValue: string) {
setOption(newValue);
}
return (
<Select onValueChange={(newValue) => handleValueChange(newValue)}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder={startingValue} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{options.map((option, idx) => (
<SelectItem value={option} key={idx}>
{option}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
);
}
export default function PortfolioSettings({
portfolioId,
@ -16,36 +67,216 @@ export default function PortfolioSettings({
// Running in the client
const router = useRouter();
// Set up state for portfolioName, portfolioBudget, portfolioGoal and portfolioStatus
// Syntax const [variable, function whos only job is to update the value of variable] = useState(initial value)
const [portfolioName, setPortfolioName] = useState(
portfolioSettingsData.name
);
function handleRename() {
// API call to rename the portfolio
// apiFunction(portfolioName)
// Update portfolio function with name is equal to portfolioName
router.refresh();
const [portfolioBudget, setPortfolioBudget] = useState<
number | string | null
>(portfolioSettingsData.budget);
const [portfolioGoal, setPortfolioGoal] = useState(
portfolioSettingsData.goal
);
const [portfolioStatus, setPortfolioStatus] = useState(
portfolioSettingsData.status
);
// Set up state for deleteModal and deleteConfirmation
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deleteConfirmationByName, setDeleteConfirmationByName] = useState("");
function handleOpenDeleteModal() {
setDeleteConfirmationByName("");
setIsDeleteModalOpen(true);
}
function handleDeleteConfirmation() {
console.log("we be deletin stuff");
// if (deleteConfirmationByName === portfolioName) {
// //apiDeletePortfolio(portfolioId)
// router.refresh();
// setIsDeleteModalOpen(false);
// } else {
// // Error if the names don't match
// console.log("Portfolio name does not match");
// }
router.push("/home");
}
// RENAMING FUNCTIONS
// Change NAME functionality - changing state
function handlePortfolioNameChange(e: React.ChangeEvent<HTMLInputElement>) {
setPortfolioName(e.target.value);
}
// Last thing we'd need to do is make that update in the db
// The onClick function called to update the NAME in the DB
function handleRenameDb() {
// apiRanameFunction(portfolioSettingsData.name)
// Update portfolioName
router.refresh();
}
// BUDGET CHANGING FUNCTIONS
// Change BUDGET functionality - changing state
function handlePortfolioBudgetUpdate(e: React.ChangeEvent<HTMLInputElement>) {
setPortfolioBudget(Number(e.target.value));
}
// The onClick function called to update the BUDGET in the DB
function handleBudgetUpdateDb() {
// apiBudgetChangeFunction(portfolioSettingsData.budget)
// Update portfolioBudget
router.refresh();
}
// CHANGING GOAL AND STATUS FUNCTIONALITY
// The onClick function called to update the GOAL in the DB
function handleGoalUpdateDb() {
// apiGoalChangeFunction(portfolioSettingsData.goal)
// Update portfolioGoal
router.refresh();
}
// The onClick function called to update the BUDGET in the DB
function handleStatusUpdateDb() {
// apiStatusChangeFunction(portfolioSettingsData.status)
// Update portfolioStatus
router.refresh();
}
// HTML to render the page
return (
<>
<div className="flex justify-center max-w-8xl w-8xl">
<ul>
<li>
Name:{" "}
<Input value={portfolioName} onChange={handlePortfolioNameChange} />{" "}
<Button onClick={handleRename}>Rename</Button>
</li>
<li>Budget: {portfolioSettingsData.budget}</li>
<li>Goal: {portfolioSettingsData.goal}</li>
<li>Status: {portfolioSettingsData.status}</li>
</ul>
<div className="p-4 mt-5 flex justify-center max-w-8xl w-8xl pt-5 bg-gray-50 rounded-lg text-brandblue">
<div className="grid grid-cols-[max-content_1fr_min-content] gap-x-[5px] gap-y-4">
{/* Row 1: Name */}
<div className="flex items-center">
<span>Name:</span>
</div>
<div className="flex items-center">
<Input value={portfolioName} onChange={handlePortfolioNameChange} />
</div>
<div className="flex items-center">
<Button className="w-full" onClick={handleRenameDb}>
Rename
</Button>
</div>
{/* Row 2: Budget */}
<div className="flex items-center">
<span>Budget:</span>
</div>
<div className="flex items-center max-w-8xl">
<Input
type="number"
value={portfolioBudget ?? undefined}
onChange={handlePortfolioBudgetUpdate}
onKeyDown={(e) => handleNumericKeyDown(e)}
/>
</div>
<div className="flex items-center">
<Button className="w-full" onClick={handleBudgetUpdateDb}>
Update
</Button>
</div>
{/* Row 3: Goal */}
<div className="flex items-center">
<span>Goal:</span>
</div>
<div className="flex items-center">
<SettingsDropdown
startingValue={portfolioGoal}
options={PortfolioGoalOptions}
setOption={setPortfolioGoal}
/>
</div>
<Button className="w-full" onClick={handleGoalUpdateDb}>
Update
</Button>
{/* Row 4: Status */}
<div className="flex items-center">
<span>Status:</span>
</div>
<div className="flex items-center">
<SettingsDropdown
startingValue={portfolioStatus}
options={PortfolioStatusOptions}
setOption={setPortfolioStatus}
/>
</div>
<Button className="w-full" onClick={handleStatusUpdateDb}>
Update
</Button>
<div className="col-span-3"> Portfolio Name: {portfolioName}</div>
<div className="col-span-3"> Portfolio Budget: {portfolioBudget}</div>
<div className="col-span-3"> Goal value: {portfolioGoal}</div>
<div> Status value: {portfolioStatus}</div>
{/* Row 5: Delete */}
<div className="col-span-2"></div>
<div className="flex items-center">
<Button
className="max-width: 100% bg-red-700"
onClick={handleOpenDeleteModal}
>
Delete Portfolio
</Button>
</div>
{/* Delete portfolio modal */}
<Dialog open={isDeleteModalOpen} onOpenChange={setIsDeleteModalOpen}>
<DialogContent>
<DialogTitle>Are you sure?</DialogTitle>
<p>
To confirm, please type the name of the portfolio (
<strong>{portfolioSettingsData.name}</strong>)
</p>
<input
type="text"
value={deleteConfirmationByName}
onChange={(e) => setDeleteConfirmationByName(e.target.value)}
placeholder="Type portfolio name"
/>
<DialogFooter>
<Button
className="bg-green-600"
onClick={() => setIsDeleteModalOpen(false)}
>
Cancel
</Button>
<Button
className="bg-red-700"
onClick={handleDeleteConfirmation}
disabled={
deleteConfirmationByName !== portfolioSettingsData.name
}
>
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
</>
);

View file

@ -10,7 +10,8 @@ export default async function PortfolioSettingsPage({
// fetch data securely on the server
// Stef's page!!!!
// 1) Rename
// 2) Delete - much harder
// 2) Update budget, status, goal
// 3) Delete - much harder
// fetch data in the server - name, budget, goal,
// pass it to a client component to render and take user input

View file

@ -1,4 +1,16 @@
import { Rating } from "./db/schema/property";
import { KeyboardEvent} from "react";
export function handleNumericKeyDown(event: KeyboardEvent<HTMLInputElement>) {
/**
* Allowing: Integers | Backspace | Tab | Delete | Left & Right arrow keys
**/
const regex = new RegExp(/(^\d*$)|(Backspace|Tab|Delete|ArrowLeft|ArrowRight|ArrowUp|ArrowDown)/);
return !event.key.match(regex) && event.preventDefault();
}
export function convertDaysToWorkingWeeks(days: number | null) {
if (days === null) {
@ -149,3 +161,5 @@ export function roundToDecimalPlaces(
const factor = 10 ** decimalPlaces;
return Math.round(number * factor) / factor;
}