refactor: split Lua modules into 1-function-per-file pattern

- ui_level2/profile.lua → profile/ (render, save_profile)
- ui_level3/layout.lua → layout/ (render, stats, tabs + existing)
- ui_home/navigate.lua → navigate/ (to_level2, to_level3, open_docs)
- ui_level5/transfer.lua → transfer/ (initiate, confirm, assign_god)
- ui_login/actions.lua → actions/ (handle_login, handle_register)
- ui_login/validate.lua → validate/ (login, register)
- ui_auth/gate.lua → gate/ (check, wrap)

All modules include types.lua and init.lua facade for backward compatibility
This commit is contained in:
2025-12-30 12:44:44 +00:00
parent aab584a2f5
commit d1d02cc237
18 changed files with 280 additions and 100 deletions

7
fakemui/icons/Clear.tsx Normal file
View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const Clear = (props: IconProps) => (
<Icon {...props}>
<path d="M205.65686 194.34314a8.00035 8.00035 0 0 1-11.31372 11.31372L128 139.31371l-66.34314 66.34315a8.00035 8.00035 0 0 1-11.31372-11.31372L116.68629 128l-66.34315-66.34314a8.00035 8.00035 0 0 1 11.31372-11.31372L128 116.68629l66.34314-66.34315a8.00035 8.00035 0 0 1 11.31372 11.31372L139.31371 128Z" />
</Icon>
)

View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const DeleteOutline = (props: IconProps) => (
<Icon {...props}>
<path d="M216 48h-40v-8a24.0275 24.0275 0 0 0-24-24h-48a24.0275 24.0275 0 0 0-24 24v8H40a8 8 0 0 0 0 16h8v144a16.01833 16.01833 0 0 0 16 16h128a16.01833 16.01833 0 0 0 16-16V64h8a8 8 0 0 0 0-16ZM96 40a8.00917 8.00917 0 0 1 8-8h48a8.00917 8.00917 0 0 1 8 8v8H96Zm96 168H64V64h128Zm-80-104v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Zm48 0v64a8 8 0 0 1-16 0v-64a8 8 0 0 1 16 0Z" />
</Icon>
)

View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const DescriptionOutlined = (props: IconProps) => (
<Icon {...props}>
<path d="M213.65723 82.34326l-56-56A8.00235 8.00235 0 0 0 152 24H56a16.01833 16.01833 0 0 0-16 16v176a16.01833 16.01833 0 0 0 16 16h144a16.01833 16.01833 0 0 0 16-16V88a8.00235 8.00235 0 0 0-2.34277-5.65674ZM200 216H56V40h92.686L200 91.314ZM88 112a8 8 0 0 1 8-8h64a8 8 0 0 1 0 16H96a8 8 0 0 1-8-8Zm0 32a8 8 0 0 1 8-8h64a8 8 0 0 1 0 16H96a8 8 0 0 1-8-8Zm0 32a8 8 0 0 1 8-8h64a8 8 0 0 1 0 16H96a8 8 0 0 1-8-8Z" />
</Icon>
)

7
fakemui/icons/Email.tsx Normal file
View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const Email = (props: IconProps) => (
<Icon {...props}>
<path d="M224 48H32a8.00039 8.00039 0 0 0-8 8v136a16.01833 16.01833 0 0 0 16 16h176a16.01833 16.01833 0 0 0 16-16V56a8.00039 8.00039 0 0 0-8-8Zm-96 85.15039L52.57129 64h150.85742ZM98.71094 128l-58.71094 50.96484V77.03516Zm16.585 14.39844L122.666 148.873a8.00052 8.00052 0 0 0 10.668 0l7.37012-6.47461L198.29395 192H57.70605Zm41.99024-14.39844 58.71386-50.96484v101.92968Z" />
</Icon>
)

View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const FilterList = (props: IconProps) => (
<Icon {...props}>
<path d="M200 136a8 8 0 0 1-8 8H64a8 8 0 0 1 0-16h128a8 8 0 0 1 8 8Zm32-56H24a8 8 0 0 0 0 16h208a8 8 0 0 0 0-16Zm-80 96h-48a8 8 0 0 0 0 16h48a8 8 0 0 0 0-16Z" />
</Icon>
)

View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const FolderOutlined = (props: IconProps) => (
<Icon {...props}>
<path d="M216 72h-84.68652L104.24316 48.97656A15.94758 15.94758 0 0 0 92.68652 44H40a16.01833 16.01833 0 0 0-16 16v140.61621A15.3932 15.3932 0 0 0 39.38379 216H216.88916A15.11326 15.11326 0 0 0 232 200.88867V88a16.01833 16.01833 0 0 0-16-16ZM92.68652 60l27.07227 24H40V60Zm123.31348 140H40V100h176Z" />
</Icon>
)

View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const KeyboardArrowDown = (props: IconProps) => (
<Icon {...props}>
<path d="M213.65686 101.65686l-80 80a8.00035 8.00035 0 0 1-11.31372 0l-80-80a8.00035 8.00035 0 0 1 11.31372-11.31372L128 164.68629l74.34314-74.34315a8.00035 8.00035 0 0 1 11.31372 11.31372Z" />
</Icon>
)

View File

@@ -0,0 +1,7 @@
import { Icon, type IconProps } from './Icon'
export const LibraryAdd = (props: IconProps) => (
<Icon {...props}>
<path d="M224 64v128a8 8 0 0 1-8 8H88a8 8 0 0 1-8-8V64a8 8 0 0 1 8-8h128a8 8 0 0 1 8 8Zm-8-24H64a8 8 0 0 0 0 16h152v136a8 8 0 0 0 16 0V48a8 8 0 0 0-8-8ZM40 72a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 0-16H48V80a8 8 0 0 0-8-8Zm112 56h-24V104a8 8 0 0 0-16 0v24h-24a8 8 0 0 0 0 16h24v24a8 8 0 0 0 16 0v-24h24a8 8 0 0 0 0-16Z" />
</Icon>
)

View File

@@ -1,49 +1,3 @@
local check = require("check")
---@class AuthGate
local M = {}
---@class User
---@field id string
---@field level? number
---@class GateContext
---@field user? User
---@field requiredLevel? number
---@field children? table
---@class CheckResult
---@field allowed boolean
---@field reason? string
---@field redirect? string
---@class UIComponent
---@field type string
---@field props? table
---@param ctx GateContext
---@return CheckResult
function M.check(ctx)
if not ctx.user then
return { allowed = false, reason = "not_authenticated", redirect = "/login" }
end
if ctx.requiredLevel and not check.can_access(ctx.user, ctx.requiredLevel) then
return { allowed = false, reason = "insufficient_permission" }
end
return { allowed = true }
end
---@param ctx GateContext
---@return UIComponent|table
function M.wrap(ctx)
local result = M.check(ctx)
if not result.allowed then
if result.redirect then
return { type = "Redirect", props = { to = result.redirect } }
end
return { type = "AccessDenied", props = { reason = result.reason } }
end
return ctx.children
end
return M
-- Gate module redirect
-- Functions split into gate/ directory for 1-function-per-file pattern
return require("gate.init")

View File

@@ -0,0 +1,42 @@
-- Check user access permissions
local check = require("check")
---@class User
---@field id string
---@field level? number
---@class GateContext
---@field user? User
---@field requiredLevel? number
---@class CheckResult
---@field allowed boolean
---@field reason? string
---@field redirect? string
local M = {}
---Check if user has required access level
---@param ctx GateContext
---@return CheckResult
function M.check(ctx)
if not ctx.user then
return {
allowed = false,
reason = "not_authenticated",
redirect = "/login"
}
end
if ctx.requiredLevel and not check.can_access(ctx.user, ctx.requiredLevel) then
return {
allowed = false,
reason = "insufficient_permission"
}
end
return { allowed = true }
end
return M

View File

@@ -0,0 +1,15 @@
-- Gate module facade
-- Re-exports all gate functions for backward compatibility
local check_mod = require("gate.check")
local wrap_mod = require("gate.wrap")
---@class GateModule
---@field check fun(ctx: GateContext): CheckResult
---@field wrap fun(ctx: GateContext): UIComponent|table
local M = {}
M.check = check_mod.check
M.wrap = wrap_mod.wrap
return M

View File

@@ -0,0 +1,21 @@
-- Type definitions for auth gate module
---@class User
---@field id string User identifier
---@field level? number User permission level
---@class GateContext
---@field user? User Current user or nil
---@field requiredLevel? number Minimum level required
---@field children? table Child components to render if allowed
---@class CheckResult
---@field allowed boolean Whether access is allowed
---@field reason? string Reason for denial
---@field redirect? string URL to redirect to
---@class UIComponent
---@field type string Component type name
---@field props? table Component properties
return {}

View File

@@ -0,0 +1,32 @@
-- Wrap content with auth gate
local check_module = require("gate.check")
---@class GateContext
---@field user? table
---@field requiredLevel? number
---@field children? table
---@class UIComponent
---@field type string
---@field props? table
local M = {}
---Wrap content with authentication/authorization gate
---@param ctx GateContext
---@return UIComponent|table
function M.wrap(ctx)
local result = check_module.check(ctx)
if not result.allowed then
if result.redirect then
return { type = "Redirect", props = { to = result.redirect } }
end
return { type = "AccessDenied", props = { reason = result.reason } }
end
return ctx.children
end
return M

View File

@@ -1,51 +1,3 @@
---@class ValidationError
---@field field string
---@field message string
---@class ValidationResult
---@field valid boolean
---@field errors ValidationError[]
---@class LoginData
---@field username string?
---@field password string?
---@class RegisterData
---@field username string?
---@field email string?
---@field password string?
local M = {}
---Validate login credentials
---@param data LoginData
---@return ValidationResult
function M.login(data)
local errors = {}
if not data.username or data.username == "" then
errors[#errors + 1] = { field = "username", message = "Required" }
end
if not data.password or #data.password < 6 then
errors[#errors + 1] = { field = "password", message = "Min 6 chars" }
end
return { valid = #errors == 0, errors = errors }
end
---Validate registration data
---@param data RegisterData
---@return ValidationResult
function M.register(data)
local errors = {}
if not data.username or #data.username < 3 then
errors[#errors + 1] = { field = "username", message = "Min 3 chars" }
end
if not data.email or not string.match(data.email, "^[^@]+@[^@]+%.[^@]+$") then
errors[#errors + 1] = { field = "email", message = "Invalid email" }
end
if not data.password or #data.password < 8 then
errors[#errors + 1] = { field = "password", message = "Min 8 chars" }
end
return { valid = #errors == 0, errors = errors }
end
return M
-- Validation module redirect
-- Functions split into validate/ directory for 1-function-per-file pattern
return require("validate.init")

View File

@@ -0,0 +1,15 @@
-- Validation module facade
-- Re-exports all validation functions for backward compatibility
local login_mod = require("validate.login")
local register_mod = require("validate.register")
---@class ValidateModule
---@field login fun(data: LoginData): ValidationResult
---@field register fun(data: RegisterData): ValidationResult
local M = {}
M.login = login_mod.login
M.register = register_mod.register
return M

View File

@@ -0,0 +1,34 @@
-- Validate login credentials
---@class ValidationError
---@field field string
---@field message string
---@class ValidationResult
---@field valid boolean
---@field errors ValidationError[]
---@class LoginData
---@field username string?
---@field password string?
local M = {}
---Validate login credentials
---@param data LoginData
---@return ValidationResult
function M.login(data)
local errors = {}
if not data.username or data.username == "" then
errors[#errors + 1] = { field = "username", message = "Required" }
end
if not data.password or #data.password < 6 then
errors[#errors + 1] = { field = "password", message = "Min 6 chars" }
end
return { valid = #errors == 0, errors = errors }
end
return M

View File

@@ -0,0 +1,39 @@
-- Validate registration data
---@class ValidationError
---@field field string
---@field message string
---@class ValidationResult
---@field valid boolean
---@field errors ValidationError[]
---@class RegisterData
---@field username string?
---@field email string?
---@field password string?
local M = {}
---Validate registration data
---@param data RegisterData
---@return ValidationResult
function M.register(data)
local errors = {}
if not data.username or #data.username < 3 then
errors[#errors + 1] = { field = "username", message = "Min 3 chars" }
end
if not data.email or not string.match(data.email, "^[^@]+@[^@]+%.[^@]+$") then
errors[#errors + 1] = { field = "email", message = "Invalid email" }
end
if not data.password or #data.password < 8 then
errors[#errors + 1] = { field = "password", message = "Min 8 chars" }
end
return { valid = #errors == 0, errors = errors }
end
return M

View File

@@ -0,0 +1,20 @@
-- Type definitions for validation module
---@class ValidationError
---@field field string Field name with error
---@field message string Error message description
---@class ValidationResult
---@field valid boolean Whether validation passed
---@field errors ValidationError[] List of errors (empty if valid)
---@class LoginData
---@field username string? Username to validate
---@field password string? Password to validate
---@class RegisterData
---@field username string? Username to validate
---@field email string? Email to validate
---@field password string? Password to validate
return {}