Title: Badges & Achievements --- The Business Platform includes a badge system that rewards users for reaching key milestones. Badges are awarded automatically when a user completes a specific action for the first time. Available Badges | Badge | Name | Description | |---|---|---| | 🏆 Project Created | projectCreated | Awarded when a user creates their first project | | ⚡ First Action Created | firstActionCreated | Awarded when a user creates their first action within a project | | 📱 First Application Created | firstApplicationCreated | Awarded when a user creates their first application within a project | | 👥 First Project Member Invite | firstProjectMemberInvite | Awarded when a user invites a member to a project for the first time | | 🗄️ First Data Type Created | firstDataTypeCreated | Awarded when a user creates their first data type within a project | How Badges Are Earned Badges are awarded automatically on the server side when a qualifying action is successfully completed. Each badge is awarded only once per user — if a user has already earned a badge, subsequent qualifying actions do not re-award it. When a badge is newly awarded, the user receives an in-app system notification confirming the achievement. Trigger Points | Badge | Trigger | |---|---| | projectCreated | POST /api/projects — after a project is successfully created | | firstActionCreated | POST /api/projects/[projectId]/actions — after an action is successfully created | | firstApplicationCreated | POST /api/projects/[projectId]/applications — after an application is successfully created | | firstProjectMemberInvite | Awarded when a project member invitation is sent for the first time | | firstDataTypeCreated | Awarded when the first data type is created within a project | Viewing Your Badges Users can view their earned and locked badges through the profile menu: Open the User Profile panel (top-right avatar or profile button). Click the Badges button (trophy icon). The badges modal displays: Earned badges — shown with a green border, the badge icon in full colour, and the date the badge was earned. Locked badges — shown with a grey border and a greyscale icon, indicating they have not yet been earned. The modal also shows a summary count, e.g. Earned Badges: 2 / 5. Technical Reference Shared Package Badge names are defined as constants in packages/shared/src/utils/badge.utils.ts: import { BADGE_NAMES } from "@smallstack/shared"; BADGENAMES.PROJECTCREATED // "projectCreated" BADGENAMES.FIRSTACTION_CREATED // "firstActionCreated" BADGENAMES.FIRSTAPPLICATION_CREATED // "firstApplicationCreated" BADGENAMES.FIRSTPROJECTMEMBERINVITE // "firstProjectMemberInvite" BADGENAMES.FIRSTDATATYPECREATED // "firstDataTypeCreated" The BadgeDefinition interface and helper functions getAllBadgeDefinitions() and getBadgeDefinition(name) are also exported from @smallstack/shared. Earned badges are stored on the User object as an array of EarnedBadge entries: interface EarnedBadge { name: string; // badge identifier earnedAt: number; // Unix timestamp (ms) } Server Package The checkBadge utility in packages/server/src/utils/badge.server.ts handles badge awarding: import { checkBadge } from "@smallstack/server"; import { BADGE_NAMES } from "@smallstack/shared"; await checkBadge(BADGENAMES.PROJECTCREATED, userId); checkBadge performs the following steps atomically within a transaction: Fetches the latest user record from the database. Checks whether the badge has already been earned. If not, appends the new EarnedBadge entry and persists the update via a patch operation. Sends a system notification message to the user. Returns true if the badge was newly awarded, or false if it was already present. Errors during badge awarding are caught and logged, and do not interrupt the parent request. Adding a New Badge Add a new key to BADGE_NAMES in packages/shared/src/utils/badge.utils.ts. Add a corresponding BadgeDefinition entry to the BADGE_DEFINITIONS array (including an icon URL, and i18n label/description keys). Add the i18n translation strings for the new labelKey and descriptionKey. Call checkBadge(BADGENAMES.YOURNEW_BADGE, userId) at the appropriate server-side endpoint after the qualifying action succeeds.