enabled i18n for all components
This commit is contained in:
@@ -2,20 +2,26 @@ import { PlusCircleIcon, HomeIcon } from "@heroicons/react/24/outline";
|
|||||||
import { YearMonth } from "../lib/db-types";
|
import { YearMonth } from "../lib/db-types";
|
||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
export interface AddLocationButtonProps {
|
export interface AddLocationButtonProps {
|
||||||
/** year and month at which the new billing location should be addes */
|
/** year and month at which the new billing location should be addes */
|
||||||
yearMonth: YearMonth
|
yearMonth: YearMonth
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddLocationButton:React.FC<AddLocationButtonProps> = ({yearMonth}) =>
|
export const AddLocationButton:React.FC<AddLocationButtonProps> = ({yearMonth}) => {
|
||||||
<div className="card card-compact card-bordered bg-base-100 shadow-s my-1">
|
|
||||||
<Link href={`/location/${ formatYearMonth(yearMonth) }/add`} className="card-body tooltip self-center" data-tip="Add a new realestate">
|
const t = useTranslations("home-page.add-location-button");
|
||||||
<span className='flex self-center mr-[-3em]' data-tip="Add a new realestate">
|
|
||||||
|
return(
|
||||||
|
<div className="card card-compact card-bordered bg-base-100 shadow-s my-1">
|
||||||
|
<Link href={`/location/${ formatYearMonth(yearMonth) }/add`} className="card-body tooltip self-center" data-tip={t("tooltip")}>
|
||||||
|
<span className='flex self-center mr-[-3em]'>
|
||||||
<HomeIcon className="h-[1em] w-[1em] cursor-pointer text-4xl" />
|
<HomeIcon className="h-[1em] w-[1em] cursor-pointer text-4xl" />
|
||||||
<PlusCircleIcon className="h-[1em] w-[1em] cursor-pointer text-xl text-green-500 ml-[-.6em] mt-[-.4em]" />
|
<PlusCircleIcon className="h-[1em] w-[1em] cursor-pointer text-xl text-green-500 ml-[-.6em] mt-[-.4em]" />
|
||||||
<span className="ml-1 mt-[.4em] text-xs text-left leading-[1.2em]">Add now<br/>realestate</span>
|
<span className="ml-1 mt-[.4em] text-xs text-left leading-[1.2em]">{t("tooltip")}</span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>;
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,18 +3,24 @@ import React from "react";
|
|||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { YearMonth } from "../lib/db-types";
|
import { YearMonth } from "../lib/db-types";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
export interface AddMonthButtonProps {
|
export interface AddMonthButtonProps {
|
||||||
yearMonth: YearMonth;
|
yearMonth: YearMonth;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddMonthButton:React.FC<AddMonthButtonProps> = ({ yearMonth }) =>
|
export const AddMonthButton:React.FC<AddMonthButtonProps> = ({ yearMonth }) => {
|
||||||
<div className="card card-compact shadow-s mb-4">
|
|
||||||
<Link href={`/year-month/${formatYearMonth(yearMonth)}/add`} className='grid self-center tooltip' data-tip="Add next month">
|
const t = useTranslations("home-page.add-month-button");
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="card card-compact shadow-s mb-4">
|
||||||
|
<Link href={`/year-month/${formatYearMonth(yearMonth)}/add`} className='grid self-center tooltip' data-tip={t("tooltip")}>
|
||||||
<span className='flex self-center mr-[-3em]'>
|
<span className='flex self-center mr-[-3em]'>
|
||||||
<CalendarDaysIcon className="h-[1em] w-[1em] cursor-pointer text-4xl" />
|
<CalendarDaysIcon className="h-[1em] w-[1em] cursor-pointer text-4xl" />
|
||||||
<PlusCircleIcon className="h-[1em] w-[1em] cursor-pointer text-xl text-green-500 ml-[-.4em] mt-[-.4em]" />
|
<PlusCircleIcon className="h-[1em] w-[1em] cursor-pointer text-xl text-green-500 ml-[-.4em] mt-[-.4em]" />
|
||||||
<span className="ml-1 mt-1 text-xs text-left leading-[1.2em]">Add next<br/>month</span>
|
<span className="ml-1 mt-1 text-xs text-left leading-[1.2em]">{t("tooltip")}</span>
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>;
|
</div>);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
import { Bill, BillingLocation } from "../lib/db-types";
|
import { Bill, BillingLocation } from "../lib/db-types";
|
||||||
import { useFormState } from "react-dom";
|
import { useFormState } from "react-dom";
|
||||||
import { Main } from "./Main";
|
import { Main } from "./Main";
|
||||||
import { deleteBillById } from "../lib/actions/billActions";
|
import { deleteBillById } from "../lib/actions/billActions";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export interface BillDeleteFormProps {
|
export interface BillDeleteFormProps {
|
||||||
bill: Bill,
|
bill: Bill,
|
||||||
@@ -17,18 +18,24 @@ export const BillDeleteForm:FC<BillDeleteFormProps> = ({ bill, location }) => {
|
|||||||
const { year, month } = location.yearMonth;
|
const { year, month } = location.yearMonth;
|
||||||
const handleAction = deleteBillById.bind(null, location._id, bill._id, year, month);
|
const handleAction = deleteBillById.bind(null, location._id, bill._id, year, month);
|
||||||
const [ state, dispatch ] = useFormState(handleAction, null);
|
const [ state, dispatch ] = useFormState(handleAction, null);
|
||||||
|
const t = useTranslations("bill-delete-form");
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div className="card card-compact card-bordered min-w-[20em] max-w-[90em] bg-base-100 shadow-s my-1">
|
<div className="card card-compact card-bordered min-w-[20em] max-w-[90em] bg-base-100 shadow-s my-1">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<form action={dispatch}>
|
<form action={dispatch}>
|
||||||
<p className="py-6 px-6">
|
<p className="py-6 px-6">
|
||||||
Please confirm deletion of bill “<strong>{bill.name}</strong>” at “<strong>{location.name}</strong>”.
|
{
|
||||||
|
t.rich("text", {
|
||||||
|
bill_name:bill.name,
|
||||||
|
location_name:location.name,
|
||||||
|
strong: (chunks:ReactNode) => `<strong>${chunks}</strong>`,
|
||||||
|
})
|
||||||
|
}
|
||||||
</p>
|
</p>
|
||||||
<div className="pt-4 text-center">
|
<div className="pt-4 text-center">
|
||||||
<button className="btn btn-primary">Confim</button>
|
<button className="btn btn-primary">{t("confirm-button")}</button>
|
||||||
<Link className="btn btn-neutral ml-3" href={`/bill/${location._id}-${bill._id}/edit/`}>Cancel</Link>
|
<Link className="btn btn-neutral ml-3" href={`/bill/${location._id}-${bill._id}/edit/`}>{t("cancel-button")}</Link>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { updateOrAddBill } from "../lib/actions/billActions";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { findDecodePdf417 } from "../lib/pdf/barcodeDecoder";
|
import { findDecodePdf417 } from "../lib/pdf/barcodeDecoder";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
// Next.js does not encode an utf-8 file name correctly when sending a form with a file attachment
|
// Next.js does not encode an utf-8 file name correctly when sending a form with a file attachment
|
||||||
// This is a workaround for that
|
// This is a workaround for that
|
||||||
@@ -25,6 +26,8 @@ export interface BillEditFormProps {
|
|||||||
|
|
||||||
export const BillEditForm:FC<BillEditFormProps> = ({ location, bill }) => {
|
export const BillEditForm:FC<BillEditFormProps> = ({ location, bill }) => {
|
||||||
|
|
||||||
|
const t = useTranslations("bill-edit-form");
|
||||||
|
|
||||||
const { _id: billID, name, paid, attachment, notes, payedAmount: initialPayedAmount, barcodeImage: initialBarcodeImage } = bill ?? { _id:undefined, name:"", paid:false, notes:"" };
|
const { _id: billID, name, paid, attachment, notes, payedAmount: initialPayedAmount, barcodeImage: initialBarcodeImage } = bill ?? { _id:undefined, name:"", paid:false, notes:"" };
|
||||||
|
|
||||||
const { yearMonth:{year: billYear, month: billMonth}, _id: locationID } = location;
|
const { yearMonth:{year: billYear, month: billMonth}, _id: locationID } = location;
|
||||||
@@ -69,12 +72,12 @@ export const BillEditForm:FC<BillEditFormProps> = ({ location, bill }) => {
|
|||||||
{
|
{
|
||||||
// don't show the delete button if we are adding a new bill
|
// don't show the delete button if we are adding a new bill
|
||||||
bill ?
|
bill ?
|
||||||
<Link href={`/bill/${locationID}-${billID}/delete/`}>
|
<Link href={`/bill/${locationID}-${billID}/delete/`} data-tip={t("delete-tooltip")}>
|
||||||
<TrashIcon className="h-[1em] w-[1em] absolute cursor-pointer text-error bottom-5 right-4 text-2xl" />
|
<TrashIcon className="h-[1em] w-[1em] absolute cursor-pointer text-error bottom-5 right-4 text-2xl" />
|
||||||
</Link> : null
|
</Link> : null
|
||||||
}
|
}
|
||||||
|
|
||||||
<input id="billName" name="billName" type="text" placeholder="Bill name" className="input input-bordered w-full" defaultValue={name} required />
|
<input id="billName" name="billName" type="text" placeholder={t("bill-name-placeholder")} className="input input-bordered w-full" defaultValue={name} required />
|
||||||
<div id="status-error" aria-live="polite" aria-atomic="true">
|
<div id="status-error" aria-live="polite" aria-atomic="true">
|
||||||
{state.errors?.billName &&
|
{state.errors?.billName &&
|
||||||
state.errors.billName.map((error: string) => (
|
state.errors.billName.map((error: string) => (
|
||||||
@@ -107,13 +110,13 @@ export const BillEditForm:FC<BillEditFormProps> = ({ location, bill }) => {
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="form-control flex-row">
|
<div className="form-control flex-row">
|
||||||
<label className="cursor-pointer label align-middle">
|
<label className="cursor-pointer label align-middle">
|
||||||
<span className="label-text mr-[1em]">Paid</span>
|
<span className="label-text mr-[1em]">{t("paid-checkbox")}</span>
|
||||||
<input id="billPaid" name="billPaid" type="checkbox" className="toggle toggle-success" defaultChecked={paid} onChange={billPaid_handleChange} />
|
<input id="billPaid" name="billPaid" type="checkbox" className="toggle toggle-success" defaultChecked={paid} onChange={billPaid_handleChange} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-control grow">
|
<div className="form-control grow">
|
||||||
<label className="cursor-pointer label grow">
|
<label className="cursor-pointer label grow">
|
||||||
<span className="label-text mx-[1em]">Amount</span>
|
<span className="label-text mx-[1em]">{t("payed-amount")}</span>
|
||||||
<input type="text" id="payedAmount" name="payedAmount" className="input input-bordered text-right w-[5em] grow" placeholder="0.00" value={payedAmount} onFocus={e => e.target.select()} onChange={payedAmount_handleChange} />
|
<input type="text" id="payedAmount" name="payedAmount" className="input input-bordered text-right w-[5em] grow" placeholder="0.00" value={payedAmount} onFocus={e => e.target.select()} onChange={payedAmount_handleChange} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,11 +137,11 @@ export const BillEditForm:FC<BillEditFormProps> = ({ location, bill }) => {
|
|||||||
<label className="cursor-pointer label p-2 grow bg-white">
|
<label className="cursor-pointer label p-2 grow bg-white">
|
||||||
<img src={barcodeImage} className="grow sm:max-w-[350px]" alt="2D Barcode" />
|
<img src={barcodeImage} className="grow sm:max-w-[350px]" alt="2D Barcode" />
|
||||||
</label>
|
</label>
|
||||||
<p className="text-xs my-1">After scanning the code make sure the information is correct.<br/>We are not liable in case of an incorrect payment.</p>
|
<p className="text-xs my-1">{t.rich('barcode-disclaimer', { br: () => <br /> })}</p>
|
||||||
</div> : null
|
</div> : null
|
||||||
}
|
}
|
||||||
|
|
||||||
<textarea id="billNotes" name="billNotes" className="textarea textarea-bordered my-2 max-w-lg w-full block" placeholder="Note" defaultValue={notes ?? ''}></textarea>
|
<textarea id="billNotes" name="billNotes" className="textarea textarea-bordered my-2 max-w-lg w-full block" placeholder={t("notes-placeholder")} defaultValue={notes ?? ''}></textarea>
|
||||||
<div id="status-error" aria-live="polite" aria-atomic="true">
|
<div id="status-error" aria-live="polite" aria-atomic="true">
|
||||||
{state.errors?.billNotes &&
|
{state.errors?.billNotes &&
|
||||||
state.errors.billNotes.map((error: string) => (
|
state.errors.billNotes.map((error: string) => (
|
||||||
@@ -149,8 +152,8 @@ export const BillEditForm:FC<BillEditFormProps> = ({ location, bill }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<button type="submit" className="btn btn-primary">Save</button>
|
<button type="submit" className="btn btn-primary">{t("save-button")}</button>
|
||||||
<Link className="btn btn-neutral ml-3" href={`/?year=${billYear}&month=${billMonth}`}>Cancel</Link>
|
<Link className="btn btn-neutral ml-3" href={`/?year=${billYear}&month=${billMonth}`}>{t("cancel-button")}</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="status-error" aria-live="polite" aria-atomic="true">
|
<div id="status-error" aria-live="polite" aria-atomic="true">
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
'client only';
|
'client only';
|
||||||
|
|
||||||
import { Cog8ToothIcon, PlusCircleIcon } from "@heroicons/react/24/outline";
|
import { Cog8ToothIcon, PlusCircleIcon } from "@heroicons/react/24/outline";
|
||||||
import { FC } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
import { BillBadge } from "./BillBadge";
|
import { BillBadge } from "./BillBadge";
|
||||||
import { BillingLocation } from "../lib/db-types";
|
import { BillingLocation } from "../lib/db-types";
|
||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { formatCurrency } from "../lib/formatStrings";
|
import { formatCurrency } from "../lib/formatStrings";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export interface LocationCardProps {
|
export interface LocationCardProps {
|
||||||
location: BillingLocation
|
location: BillingLocation
|
||||||
@@ -14,13 +15,15 @@ export interface LocationCardProps {
|
|||||||
|
|
||||||
export const LocationCard:FC<LocationCardProps> = ({location: { _id, name, yearMonth, bills }}) => {
|
export const LocationCard:FC<LocationCardProps> = ({location: { _id, name, yearMonth, bills }}) => {
|
||||||
|
|
||||||
|
const t = useTranslations("home-page.location-card");
|
||||||
|
|
||||||
// sum all the billAmounts
|
// sum all the billAmounts
|
||||||
const monthlyExpense = bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
const monthlyExpense = bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div data-key={_id } className="card card-compact card-bordered max-w-[30em] bg-base-100 border-1 border-neutral my-1">
|
<div data-key={_id } className="card card-compact card-bordered max-w-[30em] bg-base-100 border-1 border-neutral my-1">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<Link href={`/location/${_id}/edit`} className="card-subtitle tooltip" data-tip="Edit Location">
|
<Link href={`/location/${_id}/edit`} className="card-subtitle tooltip" data-tip={t("edit-card-tooltip")}>
|
||||||
<Cog8ToothIcon className="h-[1em] w-[1em] absolute cursor-pointer top-3 right-3 text-2xl" />
|
<Cog8ToothIcon className="h-[1em] w-[1em] absolute cursor-pointer top-3 right-3 text-2xl" />
|
||||||
</Link>
|
</Link>
|
||||||
<h2 className="card-title mr-[2em] text-[1rem]">{formatYearMonth(yearMonth)} {name}</h2>
|
<h2 className="card-title mr-[2em] text-[1rem]">{formatYearMonth(yearMonth)} {name}</h2>
|
||||||
@@ -28,14 +31,18 @@ export const LocationCard:FC<LocationCardProps> = ({location: { _id, name, yearM
|
|||||||
{
|
{
|
||||||
bills.map(bill => <BillBadge key={`${_id}-${bill._id}`} locationId={_id} bill={bill} />)
|
bills.map(bill => <BillBadge key={`${_id}-${bill._id}`} locationId={_id} bill={bill} />)
|
||||||
}
|
}
|
||||||
<Link href={`/bill/${_id}/add`} className="tooltip" data-tip="Add a new bill">
|
<Link href={`/bill/${_id}/add`} className="tooltip" data-tip={t("add-bill-button-tooltip")}>
|
||||||
<PlusCircleIcon className="h-[1em] w-[1em] cursor-pointer text-2xl" />
|
<PlusCircleIcon className="h-[1em] w-[1em] cursor-pointer text-2xl" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
monthlyExpense > 0 ?
|
monthlyExpense > 0 ?
|
||||||
<p>
|
<p>
|
||||||
Payed total: <strong>{ formatCurrency(monthlyExpense) }</strong>
|
{
|
||||||
|
t.rich("payed-total", {
|
||||||
|
amount: formatCurrency(monthlyExpense),
|
||||||
|
strong: (chunks:ReactNode) => `<strong>${chunks}</strong>`
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
import { BillingLocation } from "../lib/db-types";
|
import { BillingLocation } from "../lib/db-types";
|
||||||
import { deleteLocationById } from "../lib/actions/locationActions";
|
import { deleteLocationById } from "../lib/actions/locationActions";
|
||||||
import { useFormState } from "react-dom";
|
import { useFormState } from "react-dom";
|
||||||
import { gotoUrl } from "../lib/actions/navigationActions";
|
import { gotoUrl } from "../lib/actions/navigationActions";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export interface LocationDeleteFormProps {
|
export interface LocationDeleteFormProps {
|
||||||
/** location which should be deleted */
|
/** location which should be deleted */
|
||||||
@@ -16,6 +17,8 @@ export const LocationDeleteForm:FC<LocationDeleteFormProps> = ({ location }) =>
|
|||||||
{
|
{
|
||||||
const handleAction = deleteLocationById.bind(null, location._id, location.yearMonth);
|
const handleAction = deleteLocationById.bind(null, location._id, location.yearMonth);
|
||||||
const [ state, dispatch ] = useFormState(handleAction, null);
|
const [ state, dispatch ] = useFormState(handleAction, null);
|
||||||
|
const t = useTranslations("location-delete-form");
|
||||||
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
gotoUrl(`/location/${location._id}/edit/`);
|
gotoUrl(`/location/${location._id}/edit/`);
|
||||||
@@ -26,11 +29,16 @@ export const LocationDeleteForm:FC<LocationDeleteFormProps> = ({ location }) =>
|
|||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<form action={dispatch}>
|
<form action={dispatch}>
|
||||||
<p className="py-6 px-6">
|
<p className="py-6 px-6">
|
||||||
Please confirm deletion of location “<strong>{location.name}</strong>”.
|
{
|
||||||
|
t.rich("text", {
|
||||||
|
name:location.name,
|
||||||
|
strong: (chunks:ReactNode) => `<strong>${chunks}</strong>`,
|
||||||
|
})
|
||||||
|
}
|
||||||
</p>
|
</p>
|
||||||
<div className="pt-4 text-center">
|
<div className="pt-4 text-center">
|
||||||
<button className="btn btn-primary w-[5.5em]">Confim</button>
|
<button className="btn btn-primary w-[5.5em]">{t("confirm-button")}</button>
|
||||||
<Link className="btn btn-neutral w-[5.5em] ml-3" href={`/location/${location._id}/edit/`}>Cancel</Link>
|
<Link className="btn btn-neutral w-[5.5em] ml-3" href={`/location/${location._id}/edit/`}>{t("cancel-button")}</Link>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { BillingLocation, YearMonth } from "../lib/db-types";
|
|||||||
import { updateOrAddLocation } from "../lib/actions/locationActions";
|
import { updateOrAddLocation } from "../lib/actions/locationActions";
|
||||||
import { useFormState } from "react-dom";
|
import { useFormState } from "react-dom";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export type LocationEditFormProps = {
|
export type LocationEditFormProps = {
|
||||||
/** location which should be edited */
|
/** location which should be edited */
|
||||||
@@ -24,6 +25,7 @@ export const LocationEditForm:FC<LocationEditFormProps> = ({ location, yearMonth
|
|||||||
const initialState = { message: null, errors: {} };
|
const initialState = { message: null, errors: {} };
|
||||||
const handleAction = updateOrAddLocation.bind(null, location?._id, location?.yearMonth ?? yearMonth);
|
const handleAction = updateOrAddLocation.bind(null, location?._id, location?.yearMonth ?? yearMonth);
|
||||||
const [ state, dispatch ] = useFormState(handleAction, initialState);
|
const [ state, dispatch ] = useFormState(handleAction, initialState);
|
||||||
|
const t = useTranslations("location-edit-form");
|
||||||
|
|
||||||
let { year, month } = location ? location.yearMonth : yearMonth;
|
let { year, month } = location ? location.yearMonth : yearMonth;
|
||||||
|
|
||||||
@@ -33,11 +35,11 @@ export const LocationEditForm:FC<LocationEditFormProps> = ({ location, yearMonth
|
|||||||
<form action={dispatch}>
|
<form action={dispatch}>
|
||||||
{
|
{
|
||||||
location &&
|
location &&
|
||||||
<Link href={`/location/${location._id}/delete`} className="absolute bottom-5 right-4 tooltip" data-tip="Delete Location">
|
<Link href={`/location/${location._id}/delete`} className="absolute bottom-5 right-4 tooltip" data-tip={t("delete-tooltip")}>
|
||||||
<TrashIcon className="h-[1em] w-[1em] text-error text-2xl" />
|
<TrashIcon className="h-[1em] w-[1em] text-error text-2xl" />
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
<input id="locationName" name="locationName" type="text" placeholder="Realestate name" className="input input-bordered w-full" defaultValue={location?.name ?? ""} />
|
<input id="locationName" name="locationName" type="text" placeholder={t("location-name-placeholder")} className="input input-bordered w-full" defaultValue={location?.name ?? ""} />
|
||||||
<div id="status-error" aria-live="polite" aria-atomic="true">
|
<div id="status-error" aria-live="polite" aria-atomic="true">
|
||||||
{state.errors?.locationName &&
|
{state.errors?.locationName &&
|
||||||
state.errors.locationName.map((error: string) => (
|
state.errors.locationName.map((error: string) => (
|
||||||
@@ -47,7 +49,7 @@ export const LocationEditForm:FC<LocationEditFormProps> = ({ location, yearMonth
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<textarea id="locationNotes" name="locationNotes" className="textarea textarea-bordered my-1 w-full block h-[8em]" placeholder="Description" defaultValue={location?.notes ?? ""}></textarea>
|
<textarea id="locationNotes" name="locationNotes" className="textarea textarea-bordered my-1 w-full block h-[8em]" placeholder={t("notes-placeholder")} defaultValue={location?.notes ?? ""}></textarea>
|
||||||
<div id="status-error" aria-live="polite" aria-atomic="true">
|
<div id="status-error" aria-live="polite" aria-atomic="true">
|
||||||
{state.errors?.locationNotes &&
|
{state.errors?.locationNotes &&
|
||||||
state.errors.locationNotes.map((error: string) => (
|
state.errors.locationNotes.map((error: string) => (
|
||||||
@@ -66,8 +68,8 @@ export const LocationEditForm:FC<LocationEditFormProps> = ({ location, yearMonth
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<button className="btn btn-primary w-[5.5em]">Save</button>
|
<button className="btn btn-primary w-[5.5em]">{t("save-button")}</button>
|
||||||
<Link className="btn btn-neutral w-[5.5em] ml-3" href={`/?year=${year}&month=${month}`}>Cancel</Link>
|
<Link className="btn btn-neutral w-[5.5em] ml-3" href={`/?year=${year}&month=${month}`}>{t("cancel-button")}</Link>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FC, useEffect, useRef } from "react";
|
|||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { YearMonth } from "../lib/db-types";
|
import { YearMonth } from "../lib/db-types";
|
||||||
import { formatCurrency } from "../lib/formatStrings";
|
import { formatCurrency } from "../lib/formatStrings";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export interface MonthCardProps {
|
export interface MonthCardProps {
|
||||||
yearMonth: YearMonth,
|
yearMonth: YearMonth,
|
||||||
@@ -17,6 +17,7 @@ export interface MonthCardProps {
|
|||||||
export const MonthCard:FC<MonthCardProps> = ({ yearMonth, children, monthlyExpense, expanded, onToggle }) => {
|
export const MonthCard:FC<MonthCardProps> = ({ yearMonth, children, monthlyExpense, expanded, onToggle }) => {
|
||||||
|
|
||||||
const elRef = useRef<HTMLDivElement>(null);
|
const elRef = useRef<HTMLDivElement>(null);
|
||||||
|
const t = useTranslations("home-page.month-card");
|
||||||
|
|
||||||
// Setting the `month` will activate the accordion belonging to that month
|
// Setting the `month` will activate the accordion belonging to that month
|
||||||
// If the accordion is already active, it will collapse it
|
// If the accordion is already active, it will collapse it
|
||||||
@@ -37,7 +38,7 @@ export const MonthCard:FC<MonthCardProps> = ({ yearMonth, children, monthlyExpen
|
|||||||
{
|
{
|
||||||
monthlyExpense>0 ?
|
monthlyExpense>0 ?
|
||||||
<p className="text-xs font-medium">
|
<p className="text-xs font-medium">
|
||||||
Total monthly expenditure: <strong>{ formatCurrency(monthlyExpense) }</strong>
|
{t("payed-total-label")} <strong>{ formatCurrency(monthlyExpense) }</strong>
|
||||||
</p> : null
|
</p> : null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { signIn } from "next-auth/react"
|
import { signIn } from "next-auth/react"
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
const providerLogo = (provider: {id:string, name:string}) => {
|
const providerLogo = (provider: {id:string, name:string}) => {
|
||||||
@@ -14,10 +15,15 @@ const providerLogo = (provider: {id:string, name:string}) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SignInButton:React.FC<{ provider: {id:string, name:string} }> = ({ provider }) =>
|
export const SignInButton:React.FC<{ provider: {id:string, name:string} }> = ({ provider }) => {
|
||||||
<button className="btn btn-neutral" onClick={() => signIn(provider.id, { callbackUrl:"https://rezije.app/" }) }>
|
|
||||||
|
const t = useTranslations("login-page");
|
||||||
|
|
||||||
|
return(
|
||||||
|
<button className="btn btn-neutral" onClick={() => signIn(provider.id, { callbackUrl:"https://rezije.app/" }) }>
|
||||||
<Image alt="Provider Logo" loading="lazy" height="24" width="24" id="provider-logo-dark" src={providerLogo(provider)} />
|
<Image alt="Provider Logo" loading="lazy" height="24" width="24" id="provider-logo-dark" src={providerLogo(provider)} />
|
||||||
<span>Sign in with {provider.name}</span>
|
<span>
|
||||||
</button>
|
{t("sign-in-button")} {provider.name}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -40,6 +40,55 @@
|
|||||||
"video-url": "/welcome-demo-vp9-25fps-1500bps.webm",
|
"video-url": "/welcome-demo-vp9-25fps-1500bps.webm",
|
||||||
"image-url": "/bar-code-demo.png",
|
"image-url": "/bar-code-demo.png",
|
||||||
"video-title": "Demo osnovnih koraka u aplikaciji"
|
"video-title": "Demo osnovnih koraka u aplikaciji"
|
||||||
|
},
|
||||||
|
"sign-in-button": "Sign in with"
|
||||||
|
},
|
||||||
|
"home-page": {
|
||||||
|
"add-location-button": {
|
||||||
|
"tooltop": "Add a new realestate"
|
||||||
|
},
|
||||||
|
"add-month-button": {
|
||||||
|
"tooltop": "Add next mont"
|
||||||
|
},
|
||||||
|
"location-card": {
|
||||||
|
"edit-card-tooltip": "Edit realestate",
|
||||||
|
"add-bill-button-tooltip": "Add a new bill",
|
||||||
|
"payed-total": "Payed total: <strong>{amount}</strong>"
|
||||||
|
},
|
||||||
|
"month-card": {
|
||||||
|
"payed-total-label": "Total monthly expenditure:"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"bill-delete-form":
|
||||||
|
{
|
||||||
|
"text": "Please confirm deletion of bill “<strong>{bill_name}</strong>” at “<strong>{location_name}</strong>”.",
|
||||||
|
"cancel-button": "Cancel",
|
||||||
|
"confirm-button": "Confirm"
|
||||||
|
},
|
||||||
|
"bill-edit-form":
|
||||||
|
{
|
||||||
|
"bill-name-placeholder": "Bill name",
|
||||||
|
"paid-checkbox":"Paid",
|
||||||
|
"payed-amount": "Amount",
|
||||||
|
"barcode-disclaimer": "After scanning the code make sure the information is correct.<br/>We are not liable in case of an incorrect payment.",
|
||||||
|
"notes-placeholder": "Notes",
|
||||||
|
"save-button": "Save",
|
||||||
|
"cancel-button": "Cancel",
|
||||||
|
"delete-tooltip": "Delete bill"
|
||||||
|
},
|
||||||
|
"location-delete-form":
|
||||||
|
{
|
||||||
|
"text": "Please confirm deletion of realestate “<strong>{name}</strong>””.",
|
||||||
|
"cancel-button": "Cancel",
|
||||||
|
"confirm-button": "Confirm"
|
||||||
|
},
|
||||||
|
"location-edit-form":
|
||||||
|
{
|
||||||
|
"location-name-placeholder": "Realestate name",
|
||||||
|
"notes-placeholder": "Notes",
|
||||||
|
"save-button": "Save",
|
||||||
|
"cancel-button": "Cancel",
|
||||||
|
"delete-tooltip": "Delete realestate"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "rezije",
|
"name": "evidencija-rezija",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|||||||
Reference in New Issue
Block a user