Extend toast notifications to all forms (bills and locations)

- Update billActions to redirect with toast messages
  - billSaved: when bill is created or updated
  - billDeleted: when bill is deleted
- Update locationActions to redirect with toast messages
  - locationSaved: when location is created or updated
  - locationDeleted: when location is deleted
- Enhance MonthLocationList to check for all toast parameters
  - Consolidated success message handling for all operations
  - Clean up all URL parameters after showing toast
- Add translations for all success messages (EN and HR)
  - Bill saved/deleted messages
  - Location saved/deleted messages

User experience: All forms now redirect to home with toast
notifications, providing consistent feedback across the app.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Knee Cola
2025-11-17 19:06:10 +01:00
parent 5bbf80c2ae
commit fa1d04480f
5 changed files with 60 additions and 26 deletions

View File

@@ -6,8 +6,8 @@ import { Bill, BilledTo, BillAttachment, BillingLocation, YearMonth } from '../d
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { withUser } from '@/app/lib/auth'; import { withUser } from '@/app/lib/auth';
import { AuthenticatedUser } from '../types/next-auth'; import { AuthenticatedUser } from '../types/next-auth';
import { gotoHome } from './navigationActions'; import { gotoHome, gotoHomeWithMessage } from './navigationActions';
import { getTranslations } from "next-intl/server"; import { getTranslations, getLocale } from "next-intl/server";
import { IntlTemplateFn } from '@/app/i18n'; import { IntlTemplateFn } from '@/app/i18n';
export type State = { export type State = {
@@ -278,8 +278,9 @@ export const updateOrAddBill = withUser(async (user:AuthenticatedUser, locationI
} }
} }
} }
if(billYear && billMonth ) { if(billYear && billMonth) {
await gotoHome({ year: billYear, month: billMonth }); const locale = await getLocale();
await gotoHomeWithMessage(locale, 'billSaved');
} }
}) })
/* /*
@@ -446,6 +447,6 @@ export const deleteBillById = withUser(async (user:AuthenticatedUser, locationID
}); });
} }
await gotoHome({year, month}); const locale = await getLocale();
return { message: null }; await gotoHomeWithMessage(locale, 'billDeleted');
}); });

View File

@@ -6,10 +6,10 @@ import { BillingLocation, YearMonth } from '../db-types';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { withUser } from '@/app/lib/auth'; import { withUser } from '@/app/lib/auth';
import { AuthenticatedUser } from '../types/next-auth'; import { AuthenticatedUser } from '../types/next-auth';
import { gotoHome } from './navigationActions'; import { gotoHome, gotoHomeWithMessage } from './navigationActions';
import { unstable_noStore as noStore } from 'next/cache'; import { unstable_noStore as noStore } from 'next/cache';
import { IntlTemplateFn } from '@/app/i18n'; import { IntlTemplateFn } from '@/app/i18n';
import { getTranslations } from "next-intl/server"; import { getTranslations, getLocale } from "next-intl/server";
export type State = { export type State = {
errors?: { errors?: {
@@ -221,12 +221,10 @@ export const updateOrAddLocation = withUser(async (user:AuthenticatedUser, locat
} }
} }
if(yearMonth) await gotoHome(yearMonth); if(yearMonth) {
const locale = await getLocale();
return { await gotoHomeWithMessage(locale, 'locationSaved');
message: null, }
errors: undefined,
};
}); });
@@ -403,9 +401,6 @@ export const deleteLocationById = withUser(async (user:AuthenticatedUser, locati
await dbClient.collection<BillingLocation>("lokacije").deleteOne({ _id: locationID, userId }); await dbClient.collection<BillingLocation>("lokacije").deleteOne({ _id: locationID, userId });
} }
await gotoHome(yearMonth); const locale = await getLocale();
await gotoHomeWithMessage(locale, 'locationDeleted');
return {
message: null
};
}) })

View File

@@ -47,13 +47,43 @@ export const MonthLocationList:React.FC<MonthLocationListProps > = ({
initialMonth ? parseInt(initialMonth as string) : -1 // no month is expanded initialMonth ? parseInt(initialMonth as string) : -1 // no month is expanded
); );
// Check for profile saved success message // Check for success messages
React.useEffect(() => { React.useEffect(() => {
const params = new URLSearchParams(search.toString());
let messageShown = false;
if (search.get('profileSaved') === 'true') { if (search.get('profileSaved') === 'true') {
toast.success(t("profile-saved-message"), { theme: "dark" }); toast.success(t("profile-saved-message"), { theme: "dark" });
// Clean up URL parameter
const params = new URLSearchParams(search.toString());
params.delete('profileSaved'); params.delete('profileSaved');
messageShown = true;
}
if (search.get('billSaved') === 'true') {
toast.success(t("bill-saved-message"), { theme: "dark" });
params.delete('billSaved');
messageShown = true;
}
if (search.get('billDeleted') === 'true') {
toast.success(t("bill-deleted-message"), { theme: "dark" });
params.delete('billDeleted');
messageShown = true;
}
if (search.get('locationSaved') === 'true') {
toast.success(t("location-saved-message"), { theme: "dark" });
params.delete('locationSaved');
messageShown = true;
}
if (search.get('locationDeleted') === 'true') {
toast.success(t("location-deleted-message"), { theme: "dark" });
params.delete('locationDeleted');
messageShown = true;
}
// Clean up URL parameters if any message was shown
if (messageShown) {
const newSearch = params.toString(); const newSearch = params.toString();
router.replace(newSearch ? `?${newSearch}` : '/'); router.replace(newSearch ? `?${newSearch}` : '/');
} }

View File

@@ -74,7 +74,11 @@
"empty-state-title": "No Barcode Data Found", "empty-state-title": "No Barcode Data Found",
"empty-state-message": "No bills with 2D barcodes found for {yearMonth}" "empty-state-message": "No bills with 2D barcodes found for {yearMonth}"
}, },
"profile-saved-message": "Profile updated successfully" "profile-saved-message": "Profile updated successfully",
"bill-saved-message": "Bill saved successfully",
"bill-deleted-message": "Bill deleted successfully",
"location-saved-message": "Location saved successfully",
"location-deleted-message": "Location deleted successfully"
}, },
"bill-delete-form": { "bill-delete-form": {
"text": "Please confirm deletion of bill \"<strong>{bill_name}</strong>\" at \"<strong>{location_name}</strong>\".", "text": "Please confirm deletion of bill \"<strong>{bill_name}</strong>\" at \"<strong>{location_name}</strong>\".",

View File

@@ -74,7 +74,11 @@
"empty-state-title": "Nema Podataka o Barkodovima", "empty-state-title": "Nema Podataka o Barkodovima",
"empty-state-message": "Nema računa s 2D barkodovima za {yearMonth}" "empty-state-message": "Nema računa s 2D barkodovima za {yearMonth}"
}, },
"profile-saved-message": "Profil uspješno ažuriran" "profile-saved-message": "Profil uspješno ažuriran",
"bill-saved-message": "Račun uspješno spremljen",
"bill-deleted-message": "Račun uspješno obrisan",
"location-saved-message": "Nekretnina uspješno spremljena",
"location-deleted-message": "Nekretnina uspješno obrisana"
}, },
"bill-delete-form": { "bill-delete-form": {
"text": "Molim potvrdi brisanje računa \"<strong>{bill_name}</strong>\" koji pripada nekretnini \"<strong>{location_name}</strong>\".", "text": "Molim potvrdi brisanje računa \"<strong>{bill_name}</strong>\" koji pripada nekretnini \"<strong>{location_name}</strong>\".",