diff --git a/fakemui/icons/ClipboardCheck.tsx b/fakemui/icons/ClipboardCheck.tsx new file mode 100644 index 000000000..df9eea59b --- /dev/null +++ b/fakemui/icons/ClipboardCheck.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const ClipboardCheck = (props: IconProps) => ( + + + + + +) diff --git a/fakemui/icons/UserMinus.tsx b/fakemui/icons/UserMinus.tsx new file mode 100644 index 000000000..92ed7b168 --- /dev/null +++ b/fakemui/icons/UserMinus.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const UserMinus = (props: IconProps) => ( + + + + + +) diff --git a/fakemui/icons/index.ts b/fakemui/icons/index.ts index 6814d0fa9..896742f2a 100644 --- a/fakemui/icons/index.ts +++ b/fakemui/icons/index.ts @@ -53,6 +53,8 @@ export { GearSix } from './GearSix' export { User } from './User' export { UserCircle } from './UserCircle' export { UserPlus } from './UserPlus' +export { UserMinus } from './UserMinus' +export { UserX } from './UserX' export { Users } from './Users' export { UserSwitch } from './UserSwitch' export { Menu } from './Menu' @@ -149,6 +151,7 @@ export { Archive } from './Archive' export { Bug } from './Bug' export { Gavel } from './Gavel' export { Clipboard } from './Clipboard' +export { ClipboardCheck } from './ClipboardCheck' export { Package } from './Package' export { Layers } from './Layers' export { Tag } from './Tag' diff --git a/packages/audit_log/seed/scripts/formatting/mappings.lua b/packages/audit_log/seed/scripts/formatting/mappings.lua index f7b0f1de2..95e933db1 100644 --- a/packages/audit_log/seed/scripts/formatting/mappings.lua +++ b/packages/audit_log/seed/scripts/formatting/mappings.lua @@ -14,10 +14,12 @@ M.operationColors = { --- Resource type to icon mapping ---@type table -M.resourceIcons = { - user = "User", - credential = "ShieldCheck", - default = "ChartLine" -} +M.resourceIcons = { + user = "User", + credential = "ShieldCheck", + post = "ClipboardCheck", + settings = "Settings", + default = "ChartLine" +} return M diff --git a/packages/nav_menu/seed/scripts/menu.lua b/packages/nav_menu/seed/scripts/menu.lua index 0c966fec7..46e5b933e 100644 --- a/packages/nav_menu/seed/scripts/menu.lua +++ b/packages/nav_menu/seed/scripts/menu.lua @@ -17,13 +17,30 @@ local M = {} ---@class User ---@field level? number ----@class MenuProps ----@field items MenuItem[] ----@field user User - ----@param props MenuProps ----@return UIComponent -function M.render(props) +---@class MenuProps +---@field items MenuItem[] +---@field user User + +---@class FlexProps +---@field className string + +---@class ButtonProps +---@field variant string +---@field text string +---@field onClick string +---@field data? string + +---@class DropdownMenuTriggerProps +---@field text string + +---@class DropdownMenuItemProps +---@field text string +---@field onClick string +---@field data? string + +---@param props MenuProps +---@return UIComponent +function M.render(props) local items = {} for _, item in ipairs(props.items or {}) do if M.can_show(props.user, item) then diff --git a/packages/nav_menu/seed/scripts/tests/items.cases.json b/packages/nav_menu/seed/scripts/tests/items.cases.json new file mode 100644 index 000000000..6ff3fdd5a --- /dev/null +++ b/packages/nav_menu/seed/scripts/tests/items.cases.json @@ -0,0 +1,10 @@ +{ + "menu_item": [ + { "desc": "without icon", "label": "Home", "path": "/", "icon": null }, + { "desc": "with icon", "label": "Settings", "path": "/settings", "icon": "gear" } + ], + "menu_group": [ + { "desc": "empty group", "label": "Admin", "children": null, "icon": null }, + { "desc": "empty children array", "label": "Settings", "children": [], "icon": "gear" } + ] +} diff --git a/packages/nav_menu/seed/scripts/tests/items.test.lua b/packages/nav_menu/seed/scripts/tests/items.test.lua index d9a152655..aaea75313 100644 --- a/packages/nav_menu/seed/scripts/tests/items.test.lua +++ b/packages/nav_menu/seed/scripts/tests/items.test.lua @@ -1,35 +1,42 @@ --- Items tests for nav_menu package --- Tests menu item builder functions - -local menu_item = require("items/item") -local menu_group = require("items/group") -local menu_divider = require("items/divider") - -describe("Menu Item Builders", function() - describe("menu_item", function() - it.each({ - { label = "Home", path = "/", icon = nil, desc = "without icon" }, - { label = "Settings", path = "/settings", icon = "gear", desc = "with icon" }, - })("should create item $desc", function(testCase) - local result = menu_item(testCase.label, testCase.path, testCase.icon) - expect(result.type).toBe("menu_item") - expect(result.label).toBe(testCase.label) - expect(result.path).toBe(testCase.path) +-- Items tests for nav_menu package +-- Tests menu item builder functions + +---@class MenuItemCase +---@field label string +---@field path string +---@field icon string|nil +---@field desc string + +---@class MenuGroupCase +---@field label string +---@field children table|nil +---@field icon string|nil +---@field desc string + +local menu_item = require("items/item") +local menu_group = require("items/group") +local menu_divider = require("items/divider") +local cases = load_cases("items.cases.json") + +describe("Menu Item Builders", function() + describe("menu_item", function() + it.each(cases.menu_item, "$desc", function(testCase) + local result = menu_item(testCase.label, testCase.path, testCase.icon) + expect(result.type).toBe("menu_item") + expect(result.label).toBe(testCase.label) + expect(result.path).toBe(testCase.path) if testCase.icon then expect(result.icon).toBe(testCase.icon) end end) end) - describe("menu_group", function() - it.each({ - { label = "Admin", children = nil, icon = nil, desc = "empty group" }, - { label = "Settings", children = {}, icon = "gear", desc = "empty children array" }, - })("should create group $desc", function(testCase) - local result = menu_group(testCase.label, testCase.children, testCase.icon) - expect(result.type).toBe("menu_group") - expect(result.label).toBe(testCase.label) - expect(result.children).toBeType("table") + describe("menu_group", function() + it.each(cases.menu_group, "$desc", function(testCase) + local result = menu_group(testCase.label, testCase.children, testCase.icon) + expect(result.type).toBe("menu_group") + expect(result.label).toBe(testCase.label) + expect(result.children).toBeType("table") end) it("should include children in group", function() diff --git a/packages/nav_menu/seed/scripts/tests/menu.test.lua b/packages/nav_menu/seed/scripts/tests/menu.test.lua index d6e55b79f..8b7dd9916 100644 --- a/packages/nav_menu/seed/scripts/tests/menu.test.lua +++ b/packages/nav_menu/seed/scripts/tests/menu.test.lua @@ -20,15 +20,29 @@ ---@field expected boolean ---@field desc string +---@class MenuItemRenderTestCase +---@field item MenuItem +---@field expectedType string +---@field expectedText? string +---@field expectedVariant? string +---@field expectedChildren? integer +---@field desc string + ---@class MenuRenderTestCase ---@field props MenuRenderProps ---@field expectedChildren integer ---@field desc string +---@class MenuCases +---@field can_show MenuShowTestCase[] +---@field item MenuItemRenderTestCase[] +---@field render MenuRenderTestCase[] + describe("Menu", function() -- Mock check module - ---@type { can_show: MenuShowTestCase[], render: MenuRenderTestCase[] } + ---@type MenuCases local cases = load_cases("menu.cases.json") + local it_each = require("lua_test.it_each") before(function() -- Create mock for check.can_access @@ -42,38 +56,39 @@ describe("Menu", function() local menu = require("menu") describe("can_show", function() - it.each(cases.can_show, "$desc", function(testCase) - local result = menu.can_show(testCase.user, testCase.item) - expect(result).toBe(testCase.expected) + it_each(cases.can_show, "$desc", function(testCase) + ---@type MenuShowTestCase + local tc = testCase + local result = menu.can_show(tc.user, tc.item) + expect(result).toBe(tc.expected) end) end) - - describe("item", function() - it("should render button for simple item", function() - local result = menu.item({ label = "Home", path = "/" }) - expect(result.type).toBe("Button") - expect(result.props.text).toBe("Home") - expect(result.props.variant).toBe("ghost") - end) - - it("should render dropdown for item with children", function() - local result = menu.item({ - label = "Settings", - children = { - { label = "Profile", path = "/profile" }, - { label = "Security", path = "/security" } - } - }) - expect(result.type).toBe("DropdownMenu") - expect(#result.children).toBe(2) - end) - end) - + + describe("item", function() + it_each(cases.item, "$desc", function(testCase) + ---@type MenuItemRenderTestCase + local tc = testCase + local result = menu.item(tc.item) + expect(result.type).toBe(tc.expectedType) + if tc.expectedText then + expect(result.props.text).toBe(tc.expectedText) + end + if tc.expectedVariant then + expect(result.props.variant).toBe(tc.expectedVariant) + end + if tc.expectedChildren then + expect(#result.children).toBe(tc.expectedChildren) + end + end) + end) + describe("render", function() - it.each(cases.render, "$desc", function(testCase) - local result = menu.render(testCase.props) + it_each(cases.render, "$desc", function(testCase) + ---@type MenuRenderTestCase + local tc = testCase + local result = menu.render(tc.props) expect(result.type).toBe("Flex") - expect(#result.children).toBe(testCase.expectedChildren) + expect(#result.children).toBe(tc.expectedChildren) end) end) end) diff --git a/packages/ui_level2/seed/scripts/profile/render.lua b/packages/ui_level2/seed/scripts/profile/render.lua index 07ae84133..5161e57e1 100644 --- a/packages/ui_level2/seed/scripts/profile/render.lua +++ b/packages/ui_level2/seed/scripts/profile/render.lua @@ -14,54 +14,58 @@ ---@class ButtonProps ---@field text string ---@field onClick string +---@field variant? string ---@class UserInfo ---@field username string ---@field email? string - ----@class RenderContext ----@field user UserInfo + +---@class RenderContext +---@field user UserInfo local M = {} ---Renders the user profile card with form inputs ---@param ctx RenderContext ---@return UIComponent -function M.render(ctx) - return { - type = "Card", - children = { - { - type = "CardHeader", - children = { - { type = "CardTitle", props = { text = "Your Profile" } } - } - }, - { - type = "CardContent", - children = { - { - type = "Input", - props = { - label = "Username", - value = ctx.user.username, - disabled = true - } - }, - { - type = "Input", - props = { - label = "Email", - name = "email", - value = ctx.user.email or "" - } - }, - { - type = "Button", - props = { - text = "Save Changes", - onClick = "saveProfile" - } +function M.render(ctx) + return { + type = "Card", + children = { + { + type = "CardHeader", + children = { + { type = "CardTitle", props = { text = "Your Profile" } } + } + }, + { + type = "CardContent", + children = { + { + type = "Input", + props = { + ---@type InputProps + label = "Username", + value = ctx.user.username, + disabled = true + } + }, + { + type = "Input", + props = { + ---@type InputProps + label = "Email", + name = "email", + value = ctx.user.email or "" + } + }, + { + type = "Button", + props = { + ---@type ButtonProps + text = "Save Changes", + onClick = "saveProfile" + } } } } diff --git a/packages/ui_level3/seed/scripts/moderation/init.lua b/packages/ui_level3/seed/scripts/moderation/init.lua index fbb6ea57b..2da27f257 100644 --- a/packages/ui_level3/seed/scripts/moderation/init.lua +++ b/packages/ui_level3/seed/scripts/moderation/init.lua @@ -2,14 +2,21 @@ -- Re-exports all moderation functions for backward compatibility -- Each function is defined in its own file following 1-function-per-file pattern +---@class ModerationUpdates +---@field username? string +---@field email? string +---@field role? string +---@field level? number +---@field status? string + ---@class Moderation ---@field deleteUser fun(userId: string): boolean ----@field editUser fun(userId: string, updates: table): boolean +---@field editUser fun(userId: string, updates: ModerationUpdates): boolean ---@field banUser fun(userId: string, reason: string): boolean local M = {} - --- Import all single-function modules -local deleteUser = require("moderation.delete_user") + +-- Import all single-function modules +local deleteUser = require("moderation.delete_user") local editUser = require("moderation.edit_user") local banUser = require("moderation.ban_user") diff --git a/packages/ui_level3/seed/scripts/moderation/types.lua b/packages/ui_level3/seed/scripts/moderation/types.lua index 191f85be8..c96731c57 100644 --- a/packages/ui_level3/seed/scripts/moderation/types.lua +++ b/packages/ui_level3/seed/scripts/moderation/types.lua @@ -8,11 +8,13 @@ ---@class ModerationContext ---@field user ModerationUser User object for permission checking ---@field targetId string ID of the target user for moderation action - ----@class ActionResult ----@field success boolean Whether the action was successful ----@field error string? Error message if unsuccessful ----@field action string? Action type to perform ----@field id string? ID of the affected user + +---@alias ModerationAction "delete_user"|"ban_user"|"open_edit_dialog" + +---@class ActionResult +---@field success boolean Whether the action was successful +---@field error string? Error message if unsuccessful +---@field action ModerationAction? Action type to perform +---@field id string? ID of the affected user return {} diff --git a/packages/ui_level4/seed/scripts/schemas/render.lua b/packages/ui_level4/seed/scripts/schemas/render.lua index da323da5f..dff653821 100644 --- a/packages/ui_level4/seed/scripts/schemas/render.lua +++ b/packages/ui_level4/seed/scripts/schemas/render.lua @@ -39,31 +39,78 @@ ---Renders the schemas tab with a grid of schema cards ---@param ctx SchemasRenderContext ---@return UIComponent -local function render(ctx) - local items = {} - for _, s in ipairs(ctx.schemas or {}) do - items[#items + 1] = { - type = "Card", - children = { - { type = "CardHeader", children = { { type = "CardTitle", props = { text = s.name } } } }, - { type = "CardContent", children = { - { type = "Typography", props = { text = s.description or "No description" } }, - { type = "Badge", props = { text = #(s.fields or {}) .. " fields" } } - }}, - { type = "CardFooter", children = { - { type = "Button", props = { text = "Edit", onClick = "editSchema", data = s.id } } - }} - } - } - end - return { - type = "Stack", - props = { spacing = 4 }, - children = { - { type = "Button", props = { text = "Add Schema", onClick = "addSchema" } }, - { type = "Grid", props = { cols = 2, gap = 4 }, children = items } - } - } -end +local function render(ctx) + ---@type UIComponent[] + local items = {} + for _, s in ipairs(ctx.schemas or {}) do + items[#items + 1] = { + type = "Card", + children = { + { + type = "CardHeader", + children = { + { type = "CardTitle", props = { text = s.name } } + } + }, + { + type = "CardContent", + children = { + { + type = "Typography", + props = { + ---@type TypographyProps + text = s.description or "No description" + } + }, + { + type = "Badge", + props = { + ---@type BadgeProps + text = #(s.fields or {}) .. " fields" + } + } + } + }, + { + type = "CardFooter", + children = { + { + type = "Button", + props = { + ---@type ButtonProps + text = "Edit", + onClick = "editSchema", + data = s.id + } + } + } + } + } + } + end + return { + type = "Stack", + props = { spacing = 4 }, + children = { + { + type = "Button", + props = { + ---@type ButtonProps + text = "Add Schema", + onClick = "addSchema" + } + }, + { + type = "Grid", + props = { + ---@type GridProps + cols = 2, + gap = 4 + }, + children = items + } + } + } +end return render