refactor: migrate web-app to use shared-code package
Update web-app to use @evidencija-rezija/shared-code for common types and utilities instead of maintaining duplicate copies. Changes: - Add shared-code dependency to package.json - Update all imports across 35+ files to use @evidencija-rezija/shared-code - Remove duplicate db-types.ts and shareChecksum.ts files This ensures type consistency between web-app and email-worker and reduces maintenance burden. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,8 @@ import { Suspense } from 'react';
|
|||||||
import EmailUnsubscribePage from './EmailUnsubscribePage';
|
import EmailUnsubscribePage from './EmailUnsubscribePage';
|
||||||
import { Main } from '@/app/ui/Main';
|
import { Main } from '@/app/ui/Main';
|
||||||
import { getDbClient } from '@/app/lib/dbClient';
|
import { getDbClient } from '@/app/lib/dbClient';
|
||||||
import { BillingLocation, EmailStatus } from '@/app/lib/db-types';
|
import { BillingLocation, EmailStatus } from '@evidencija-rezija/shared-code';
|
||||||
import { extractShareId, validateShareChecksum } from '@/app/lib/shareChecksum';
|
import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
export default async function Page({ params: { id } }: { params: { id: string } }) {
|
export default async function Page({ params: { id } }: { params: { id: string } }) {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Suspense } from 'react';
|
|||||||
import EmailVerifyPage from './EmailVerifyPage';
|
import EmailVerifyPage from './EmailVerifyPage';
|
||||||
import { Main } from '@/app/ui/Main';
|
import { Main } from '@/app/ui/Main';
|
||||||
import { getDbClient } from '@/app/lib/dbClient';
|
import { getDbClient } from '@/app/lib/dbClient';
|
||||||
import { BillingLocation, EmailStatus } from '@/app/lib/db-types';
|
import { BillingLocation, EmailStatus } from '@evidencija-rezija/shared-code';
|
||||||
import { extractShareId, validateShareChecksum } from '@/app/lib/shareChecksum';
|
import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
|
|
||||||
export default async function Page({ params: { id } }: { params: { id: string } }) {
|
export default async function Page({ params: { id } }: { params: { id: string } }) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { LocationEditForm } from '@/app/ui/LocationEditForm';
|
import { LocationEditForm } from '@/app/ui/LocationEditForm';
|
||||||
import { YearMonth } from '@/app/lib/db-types';
|
import { YearMonth } from '@evidencija-rezija/shared-code';
|
||||||
import { getUserSettings } from '@/app/lib/actions/userSettingsActions';
|
import { getUserSettings } from '@/app/lib/actions/userSettingsActions';
|
||||||
|
|
||||||
export default async function LocationAddPage({ yearMonth }: { yearMonth:YearMonth }) {
|
export default async function LocationAddPage({ yearMonth }: { yearMonth:YearMonth }) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FC } from "react"
|
import { FC } from "react"
|
||||||
import { Bill } from "@/app/lib/db-types"
|
import { Bill } from "@evidencija-rezija/shared-code"
|
||||||
import { TicketIcon } from "@heroicons/react/24/outline"
|
import { TicketIcon } from "@heroicons/react/24/outline"
|
||||||
|
|
||||||
export interface BillBadgeProps {
|
export interface BillBadgeProps {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import { BillingLocation, YearMonth } from "../../../../../lib/db-types";
|
import { BillingLocation, YearMonth } from "@evidencija-rezija/shared-code";
|
||||||
import { formatYearMonth } from "../../../../../lib/format";
|
import { formatYearMonth } from "../../../../../lib/format";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { InboxStackIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline';
|
import { InboxStackIcon, Square3Stack3DIcon } from '@heroicons/react/24/outline';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { YearMonth } from '../../../../../lib/db-types';
|
import { YearMonth } from '@evidencija-rezija/shared-code';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export interface MultiBillEditButtonProps {
|
export interface MultiBillEditButtonProps {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { fetchBillById } from '@/app/lib/actions/billActions';
|
import { fetchBillById } from '@/app/lib/actions/billActions';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { extractShareId, validateShareChecksum } from '@/app/lib/shareChecksum';
|
import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
import { getDbClient } from '@/app/lib/dbClient';
|
import { getDbClient } from '@/app/lib/dbClient';
|
||||||
import { BillingLocation } from '@/app/lib/db-types';
|
import { BillingLocation } from '@evidencija-rezija/shared-code';
|
||||||
|
|
||||||
export async function GET(request: Request, { params: { id } }: { params: { id: string } }) {
|
export async function GET(request: Request, { params: { id } }: { params: { id: string } }) {
|
||||||
// Parse shareId-billID format
|
// Parse shareId-billID format
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getDbClient } from '@/app/lib/dbClient';
|
import { getDbClient } from '@/app/lib/dbClient';
|
||||||
import { BillingLocation } from '@/app/lib/db-types';
|
import { BillingLocation } from '@evidencija-rezija/shared-code';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { extractShareId, validateShareChecksum } from '@/app/lib/shareChecksum';
|
import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
|
|
||||||
export async function GET(request: Request, { params: { id } }: { params: { id: string } }) {
|
export async function GET(request: Request, { params: { id } }: { params: { id: string } }) {
|
||||||
const shareId = id;
|
const shareId = id;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getDbClient } from '@/app/lib/dbClient';
|
import { getDbClient } from '@/app/lib/dbClient';
|
||||||
import { BillingLocation } from '@/app/lib/db-types';
|
import { BillingLocation } from '@evidencija-rezija/shared-code';
|
||||||
import { notFound } from 'next/navigation';
|
import { notFound } from 'next/navigation';
|
||||||
import { extractShareId, validateShareChecksum } from '@/app/lib/shareChecksum';
|
import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
|
|
||||||
export async function GET(_request: Request, { params: { id } }: { params: { id: string } }) {
|
export async function GET(_request: Request, { params: { id } }: { params: { id: string } }) {
|
||||||
// Parse shareId-billID format
|
// Parse shareId-billID format
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { gotoHomeWithMessage } from './navigationActions';
|
|||||||
import { getTranslations, getLocale } from "next-intl/server";
|
import { getTranslations, getLocale } from "next-intl/server";
|
||||||
import { IntlTemplateFn } from '@/app/i18n';
|
import { IntlTemplateFn } from '@/app/i18n';
|
||||||
import { unstable_noStore, revalidatePath } from 'next/cache';
|
import { unstable_noStore, revalidatePath } from 'next/cache';
|
||||||
import { extractShareId, validateShareChecksum } from '../shareChecksum';
|
import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
import { validatePdfFile } from '../validators/pdfValidator';
|
import { validatePdfFile } from '../validators/pdfValidator';
|
||||||
import { checkUploadRateLimit } from '../uploadRateLimiter';
|
import { checkUploadRateLimit } from '../uploadRateLimiter';
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { getDbClient } from '../dbClient';
|
import { getDbClient } from '../dbClient';
|
||||||
import { BillingLocation, EmailStatus } from '../db-types';
|
import { BillingLocation, EmailStatus } from '../db-types';
|
||||||
import { extractShareId, validateShareChecksum } from '../shareChecksum';
|
import { extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
|
|
||||||
export type EmailActionResult = {
|
export type EmailActionResult = {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { gotoHomeWithMessage } from './navigationActions';
|
|||||||
import { unstable_noStore, revalidatePath } from 'next/cache';
|
import { unstable_noStore, revalidatePath } from 'next/cache';
|
||||||
import { IntlTemplateFn } from '@/app/i18n';
|
import { IntlTemplateFn } from '@/app/i18n';
|
||||||
import { getTranslations, getLocale } from "next-intl/server";
|
import { getTranslations, getLocale } from "next-intl/server";
|
||||||
import { generateShareId, extractShareId, validateShareChecksum } from '../shareChecksum';
|
import { generateShareId, extractShareId, validateShareChecksum } from '@evidencija-rezija/shared-code';
|
||||||
import { validatePdfFile } from '../validators/pdfValidator';
|
import { validatePdfFile } from '../validators/pdfValidator';
|
||||||
import { checkUploadRateLimit } from '../uploadRateLimiter';
|
import { checkUploadRateLimit } from '../uploadRateLimiter';
|
||||||
|
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
import { unsubscribe } from "diagnostics_channel";
|
|
||||||
|
|
||||||
export interface FileAttachment {
|
|
||||||
fileName: string;
|
|
||||||
fileSize: number;
|
|
||||||
fileType: string;
|
|
||||||
fileLastModified: number;
|
|
||||||
fileContentsBase64: string;
|
|
||||||
uploadedAt: Date;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface YearMonth {
|
|
||||||
year: number;
|
|
||||||
month: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** User settings data */
|
|
||||||
export interface UserSettings {
|
|
||||||
/** user's ID */
|
|
||||||
userId: string;
|
|
||||||
/** whether enableshow IBAN payment instructions in monthly statement */
|
|
||||||
enableIbanPayment?: boolean | null;
|
|
||||||
/** owner name */
|
|
||||||
ownerName?: string | null;
|
|
||||||
/** owner street */
|
|
||||||
ownerStreet?: string | null;
|
|
||||||
/** owner town */
|
|
||||||
ownerTown?: string | null;
|
|
||||||
/** owner IBAN */
|
|
||||||
ownerIBAN?: string | null;
|
|
||||||
/** currency (ISO 4217) */
|
|
||||||
currency?: string | null;
|
|
||||||
/** whether to enable Revolut payment instructions in monthly statement */
|
|
||||||
enableRevolutPayment?: boolean | null;
|
|
||||||
/** owner Revolut payment link */
|
|
||||||
ownerRevolutProfileName?: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum EmailStatus {
|
|
||||||
/** Email is not yet verified - recipient has not yet confirmed their email address */
|
|
||||||
Unverified = "unverified",
|
|
||||||
/** Email is not yet verified - a verification request has been sent */
|
|
||||||
VerificationPending = "verification-pending",
|
|
||||||
/** sending of verification email failed */
|
|
||||||
VerificationFailed = "verification-failed",
|
|
||||||
/** Email is verified and is in good standing: emails are being successfully delivered */
|
|
||||||
Verified = "verified",
|
|
||||||
/** Recepient has unsubscribed from receiving emails via link - no further emails will be sent */
|
|
||||||
Unsubscribed = "unsubscribed"
|
|
||||||
}
|
|
||||||
|
|
||||||
/** bill object in the form returned by MongoDB */
|
|
||||||
export interface BillingLocation {
|
|
||||||
_id: string;
|
|
||||||
/** user's ID */
|
|
||||||
userId: string;
|
|
||||||
/** user's email */
|
|
||||||
userEmail?: string | null;
|
|
||||||
/** name of the location */
|
|
||||||
name: string;
|
|
||||||
/** billing period year and month */
|
|
||||||
yearMonth: YearMonth;
|
|
||||||
/** array of bills */
|
|
||||||
bills: Bill[];
|
|
||||||
/** (optional) notes */
|
|
||||||
notes: string|null;
|
|
||||||
|
|
||||||
/** (optional) method for showing payment instructions to tenant */
|
|
||||||
tenantPaymentMethod?: "none" | "iban" | "revolut" | null;
|
|
||||||
|
|
||||||
/** (optional) type of proof of payment attachment */
|
|
||||||
proofOfPaymentType: "none" | "combined" | "per-bill";
|
|
||||||
|
|
||||||
/** (optional) tenant name */
|
|
||||||
tenantName?: string | null;
|
|
||||||
/** (optional) tenant street */
|
|
||||||
tenantStreet?: string | null;
|
|
||||||
/** (optional) tenant town */
|
|
||||||
tenantTown?: string | null;
|
|
||||||
/** (optional) tenant email */
|
|
||||||
tenantEmail?: string | null;
|
|
||||||
/** (optional) tenant email status */
|
|
||||||
tenantEmailStatus?: EmailStatus | null;
|
|
||||||
/** (optional) whether to automatically notify tenant */
|
|
||||||
billFwdEnabled?: boolean | null;
|
|
||||||
/** (optional) bill forwarding strategy */
|
|
||||||
billFwdStrategy?: "when-payed" | "when-attached" | null;
|
|
||||||
/** (optional) bill forwarding status */
|
|
||||||
billFwdStatus?: "pending" | "sent" | "failed" | null;
|
|
||||||
/** (optional) whether to automatically send rent notification */
|
|
||||||
rentDueNotificationEnabled?: boolean | null;
|
|
||||||
/** (optional) day of month when rent is due (1-31) */
|
|
||||||
rentDueDay?: number | null;
|
|
||||||
/** (optional) when was the rent due notification sent */
|
|
||||||
rentDueNotificationStatus?: "sent" | "failed" | null;
|
|
||||||
/** (optional) monthly rent amount in cents */
|
|
||||||
rentAmount?: number | null;
|
|
||||||
/** (optional) whether the location has been seen by tenant */
|
|
||||||
seenByTenantAt?: Date | null;
|
|
||||||
/** (optional) utility bills proof of payment attachment */
|
|
||||||
utilBillsProofOfPayment?: FileAttachment|null;
|
|
||||||
/** (optional) rent proof of payment attachment */
|
|
||||||
rentProofOfPayment?: FileAttachment|null;
|
|
||||||
/** (optional) share link expiry timestamp */
|
|
||||||
shareTTL?: Date;
|
|
||||||
/** (optional) when tenant first visited the share link */
|
|
||||||
shareFirstVisitedAt?: Date | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum BilledTo {
|
|
||||||
Tenant = "tenant",
|
|
||||||
Landlord = "landlord"
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Bill basic data */
|
|
||||||
export interface Bill {
|
|
||||||
_id: string;
|
|
||||||
/** bill name */
|
|
||||||
name: string;
|
|
||||||
/** is the bill paid */
|
|
||||||
paid: boolean;
|
|
||||||
/** who is billed for the bill */
|
|
||||||
billedTo?: BilledTo;
|
|
||||||
/** payed amount amount in cents */
|
|
||||||
payedAmount?: number | null;
|
|
||||||
/** attached document (optional) */
|
|
||||||
attachment?: FileAttachment|null;
|
|
||||||
/**
|
|
||||||
* true if there an attachment
|
|
||||||
* @description this field enables us to send this info to the client without sending large attachment - it's an optimization
|
|
||||||
*/
|
|
||||||
hasAttachment?: boolean;
|
|
||||||
/** (optional) notes */
|
|
||||||
notes?: string|null;
|
|
||||||
/**
|
|
||||||
* (optional) image data containing PDF471 bar code
|
|
||||||
* @deprecated LEGACY FIELD - use hub3aText instead
|
|
||||||
* */
|
|
||||||
barcodeImage?:string;
|
|
||||||
/** (optional) HUB-3A text for generating PDF417 bar code */
|
|
||||||
hub3aText?:string;
|
|
||||||
/** (optional) proof of payment attachment */
|
|
||||||
proofOfPayment?: FileAttachment|null;
|
|
||||||
};
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import crypto from 'crypto';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checksum length in hex characters (16 chars = 64 bits of entropy)
|
|
||||||
*/
|
|
||||||
export const CHECKSUM_LENGTH = 16;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate share link checksum for location
|
|
||||||
* Uses HMAC-SHA256 for cryptographic integrity
|
|
||||||
*
|
|
||||||
* SECURITY: Prevents location ID enumeration while allowing stateless validation
|
|
||||||
*/
|
|
||||||
export function generateShareChecksum(locationId: string): string {
|
|
||||||
const secret = process.env.SHARE_LINK_SECRET;
|
|
||||||
|
|
||||||
if (!secret) {
|
|
||||||
throw new Error('SHARE_LINK_SECRET environment variable not configured');
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto
|
|
||||||
.createHmac('sha256', secret)
|
|
||||||
.update(locationId)
|
|
||||||
.digest('hex')
|
|
||||||
.substring(0, CHECKSUM_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate share link checksum
|
|
||||||
* Uses constant-time comparison to prevent timing attacks
|
|
||||||
*
|
|
||||||
* @param locationId - The location ID from URL
|
|
||||||
* @param providedChecksum - The checksum from URL
|
|
||||||
* @returns true if checksum is valid
|
|
||||||
*/
|
|
||||||
export function validateShareChecksum(
|
|
||||||
locationId: string,
|
|
||||||
providedChecksum: string
|
|
||||||
): boolean {
|
|
||||||
try {
|
|
||||||
const expectedChecksum = generateShareChecksum(locationId);
|
|
||||||
|
|
||||||
// Convert to buffers for timing-safe comparison
|
|
||||||
const expected = Buffer.from(expectedChecksum);
|
|
||||||
const provided = Buffer.from(providedChecksum);
|
|
||||||
|
|
||||||
// Length check (prevents timing attack on different lengths)
|
|
||||||
if (expected.length !== provided.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constant-time comparison (prevents timing attacks)
|
|
||||||
return crypto.timingSafeEqual(expected, provided);
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate combined location ID with checksum appended
|
|
||||||
* @param locationId - The MongoDB location ID (24 chars)
|
|
||||||
* @returns Combined ID: locationId + checksum (40 chars total)
|
|
||||||
*/
|
|
||||||
export function generateShareId(locationId: string): string {
|
|
||||||
const checksum = generateShareChecksum(locationId);
|
|
||||||
return locationId + checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract location ID and checksum from combined share ID
|
|
||||||
* @param shareId - Combined ID (locationId + checksum)
|
|
||||||
* @returns Object with locationId and checksum, or null if invalid format
|
|
||||||
*/
|
|
||||||
export function extractShareId(shareId: string): { locationId: string; checksum: string } | null {
|
|
||||||
// MongoDB ObjectID is 24 chars, checksum is 16 chars = 40 total
|
|
||||||
const expectedLength = 24 + CHECKSUM_LENGTH;
|
|
||||||
|
|
||||||
if (shareId.length !== expectedLength) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const locationId = shareId.substring(0, 24);
|
|
||||||
const checksum = shareId.substring(24);
|
|
||||||
|
|
||||||
return { locationId, checksum };
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { PlusCircleIcon, HomeIcon } from "@heroicons/react/24/outline";
|
import { PlusCircleIcon, HomeIcon } from "@heroicons/react/24/outline";
|
||||||
import { YearMonth } from "../lib/db-types";
|
import { YearMonth } from '@evidencija-rezija/shared-code';
|
||||||
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';
|
import { useTranslations } from 'next-intl';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { PlusCircleIcon, CalendarDaysIcon } from "@heroicons/react/24/outline";
|
import { PlusCircleIcon, CalendarDaysIcon } from "@heroicons/react/24/outline";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { YearMonth } from "../lib/db-types";
|
import { YearMonth } from '@evidencija-rezija/shared-code';
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useLocale, useTranslations } from 'next-intl';
|
import { useLocale, useTranslations } from 'next-intl';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FC } from "react"
|
import { FC } from "react"
|
||||||
import { Bill } from "@/app/lib/db-types"
|
import { Bill } from "@evidencija-rezija/shared-code"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { TicketIcon } from "@heroicons/react/24/outline"
|
import { TicketIcon } from "@heroicons/react/24/outline"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, ReactNode, useState } from "react";
|
import { FC, ReactNode, useState } from "react";
|
||||||
import { Bill, BillingLocation } from "../lib/db-types";
|
import { Bill, BillingLocation } from '@evidencija-rezija/shared-code';
|
||||||
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";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { DocumentIcon, TicketIcon, TrashIcon } from "@heroicons/react/24/outline";
|
import { DocumentIcon, TicketIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||||
import { Bill, BilledTo, BillingLocation } from "../lib/db-types";
|
import { Bill, BilledTo, BillingLocation } from '@evidencija-rezija/shared-code';
|
||||||
import React, { FC, useEffect } from "react";
|
import React, { FC, useEffect } from "react";
|
||||||
import { useFormState } from "react-dom";
|
import { useFormState } from "react-dom";
|
||||||
import { updateOrAddBill } from "../lib/actions/billActions";
|
import { updateOrAddBill } from "../lib/actions/billActions";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { fetchAllLocations } from '@/app/lib/actions/locationActions';
|
import { fetchAllLocations } from '@/app/lib/actions/locationActions';
|
||||||
import { fetchAvailableYears } from '@/app/lib/actions/monthActions';
|
import { fetchAvailableYears } from '@/app/lib/actions/monthActions';
|
||||||
import { getUserSettings } from '@/app/lib/actions/userSettingsActions';
|
import { getUserSettings } from '@/app/lib/actions/userSettingsActions';
|
||||||
import { BillingLocation, YearMonth } from '@/app/lib/db-types';
|
import { BillingLocation, YearMonth } from '@evidencija-rezija/shared-code';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { MonthLocationList } from '@/app/ui/MonthLocationList';
|
import { MonthLocationList } from '@/app/ui/MonthLocationList';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { CheckCircleIcon, Cog8ToothIcon, PlusCircleIcon, ShareIcon, BanknotesIcon, EyeIcon, TicketIcon, ShoppingCartIcon, EnvelopeIcon, ExclamationTriangleIcon, ClockIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
import { CheckCircleIcon, Cog8ToothIcon, PlusCircleIcon, ShareIcon, BanknotesIcon, EyeIcon, TicketIcon, ShoppingCartIcon, EnvelopeIcon, ExclamationTriangleIcon, ClockIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { BillBadge } from "./BillBadge";
|
import { BillBadge } from "./BillBadge";
|
||||||
import { BillingLocation, EmailStatus } from "../lib/db-types";
|
import { BillingLocation, EmailStatus } from '@evidencija-rezija/shared-code';
|
||||||
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";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, ReactNode, useState } from "react";
|
import { FC, ReactNode, useState } from "react";
|
||||||
import { BillingLocation } from "../lib/db-types";
|
import { BillingLocation } from '@evidencija-rezija/shared-code';
|
||||||
import { deleteLocationById } from "../lib/actions/locationActions";
|
import { deleteLocationById } from "../lib/actions/locationActions";
|
||||||
import { useFormState } from "react-dom";
|
import { useFormState } from "react-dom";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { TrashIcon, ExclamationTriangleIcon, ClockIcon, EnvelopeIcon, CheckCircleIcon, PencilSquareIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
import { TrashIcon, ExclamationTriangleIcon, ClockIcon, EnvelopeIcon, CheckCircleIcon, PencilSquareIcon, XCircleIcon } from "@heroicons/react/24/outline";
|
||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import { BillingLocation, UserSettings, YearMonth, EmailStatus } from "../lib/db-types";
|
import { BillingLocation, UserSettings, YearMonth, EmailStatus } from '@evidencija-rezija/shared-code';
|
||||||
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";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { FC, useEffect, useRef } from "react";
|
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 '@evidencija-rezija/shared-code';
|
||||||
import { formatCurrency } from "../lib/formatStrings";
|
import { formatCurrency } from "../lib/formatStrings";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { MonthCard } from "./MonthCard";
|
|||||||
import Pagination from "./Pagination";
|
import Pagination from "./Pagination";
|
||||||
import { LocationCard } from "./LocationCard";
|
import { LocationCard } from "./LocationCard";
|
||||||
import { PrintButton } from "./PrintButton";
|
import { PrintButton } from "./PrintButton";
|
||||||
import { BillingLocation, UserSettings, YearMonth } from "../lib/db-types";
|
import { BillingLocation, UserSettings, YearMonth } from '@evidencija-rezija/shared-code';
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
import { ToastContainer, toast } from 'react-toastify';
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { PrinterIcon } from '@heroicons/react/24/outline';
|
import { PrinterIcon } from '@heroicons/react/24/outline';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { YearMonth } from '../lib/db-types';
|
import { YearMonth } from '@evidencija-rezija/shared-code';
|
||||||
|
|
||||||
export interface PrintButtonProps {
|
export interface PrintButtonProps {
|
||||||
yearMonth: YearMonth;
|
yearMonth: YearMonth;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import { UserSettings } from "../lib/db-types";
|
import { UserSettings } from '@evidencija-rezija/shared-code';
|
||||||
import { updateUserSettings } from "../lib/actions/userSettingsActions";
|
import { updateUserSettings } from "../lib/actions/userSettingsActions";
|
||||||
import { useFormState, useFormStatus } from "react-dom";
|
import { useFormState, useFormStatus } from "react-dom";
|
||||||
import { useLocale, useTranslations } from "next-intl";
|
import { useLocale, useTranslations } from "next-intl";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { Bill } from "@/app/lib/db-types";
|
import { Bill } from "@evidencija-rezija/shared-code";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { TicketIcon } from "@heroicons/react/24/outline";
|
import { TicketIcon } from "@heroicons/react/24/outline";
|
||||||
import { useLocale } from "next-intl";
|
import { useLocale } from "next-intl";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { TicketIcon, CheckCircleIcon, XCircleIcon, DocumentIcon } from "@heroicons/react/24/outline";
|
import { TicketIcon, CheckCircleIcon, XCircleIcon, DocumentIcon } from "@heroicons/react/24/outline";
|
||||||
import { Bill, BillingLocation } from "../lib/db-types";
|
import { Bill, BillingLocation } from '@evidencija-rezija/shared-code';
|
||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { FC, useMemo, useState } from "react";
|
import { FC, useMemo, useState } from "react";
|
||||||
import { BilledTo, BillingLocation, UserSettings } from "../lib/db-types";
|
import { BilledTo, BillingLocation, UserSettings } from '@evidencija-rezija/shared-code';
|
||||||
import { formatYearMonth } from "../lib/format";
|
import { formatYearMonth } from "../lib/format";
|
||||||
import { formatCurrency, formatIban } from "../lib/formatStrings";
|
import { formatCurrency, formatIban } from "../lib/formatStrings";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"seed": "node -r dotenv/config ./scripts/seed.js"
|
"seed": "node -r dotenv/config ./scripts/seed.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@evidencija-rezija/shared-code": "^1.0.0",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@heroicons/react": "^2.0.18",
|
"@heroicons/react": "^2.0.18",
|
||||||
|
|||||||
Reference in New Issue
Block a user