mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
config: packages,lua,menu (12 files)
This commit is contained in:
10
fakemui/icons/ClipboardCheck.tsx
Normal file
10
fakemui/icons/ClipboardCheck.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import { Icon, IconProps } from './Icon'
|
||||
|
||||
export const ClipboardCheck = (props: IconProps) => (
|
||||
<Icon {...props}>
|
||||
<rect x="40" y="40" width="176" height="176" rx="8" />
|
||||
<path d="M88 24h80a8 8 0 0 1 8 8v16H80V32a8 8 0 0 1 8-8Z" />
|
||||
<polyline points="84 140 112 168 172 108" />
|
||||
</Icon>
|
||||
)
|
||||
10
fakemui/icons/UserMinus.tsx
Normal file
10
fakemui/icons/UserMinus.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import { Icon, IconProps } from './Icon'
|
||||
|
||||
export const UserMinus = (props: IconProps) => (
|
||||
<Icon {...props}>
|
||||
<circle cx="96" cy="88" r="32" />
|
||||
<path d="M32 200c0-35.3 28.7-64 64-64s64 28.7 64 64" />
|
||||
<line x1="172" y1="120" x2="224" y2="120" />
|
||||
</Icon>
|
||||
)
|
||||
@@ -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'
|
||||
|
||||
@@ -14,10 +14,12 @@ M.operationColors = {
|
||||
|
||||
--- Resource type to icon mapping
|
||||
---@type table<string, string>
|
||||
M.resourceIcons = {
|
||||
user = "User",
|
||||
credential = "ShieldCheck",
|
||||
default = "ChartLine"
|
||||
}
|
||||
M.resourceIcons = {
|
||||
user = "User",
|
||||
credential = "ShieldCheck",
|
||||
post = "ClipboardCheck",
|
||||
settings = "Settings",
|
||||
default = "ChartLine"
|
||||
}
|
||||
|
||||
return M
|
||||
|
||||
@@ -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
|
||||
|
||||
10
packages/nav_menu/seed/scripts/tests/items.cases.json
Normal file
10
packages/nav_menu/seed/scripts/tests/items.cases.json
Normal file
@@ -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" }
|
||||
]
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user