mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-28 06:46:24 +00:00
feat: refactor client codebase in progress
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -12,4 +12,4 @@
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.experimental.useTsgo": false
|
||||
}
|
||||
}
|
||||
|
||||
1
apps/lifeforge--achievements
Submodule
1
apps/lifeforge--achievements
Submodule
Submodule apps/lifeforge--achievements added at 207155caa7
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@lifeforge/apps",
|
||||
"private": true,
|
||||
"description": "LifeForge modules",
|
||||
"dependencies": {
|
||||
"@lifeforge/TedMeadow--lang-ru": "workspace:*",
|
||||
"@lifeforge/lifeforge--achievements": "workspace:*"
|
||||
}
|
||||
"name": "@lifeforge/apps",
|
||||
"private": true,
|
||||
"description": "LifeForge modules",
|
||||
"dependencies": {
|
||||
"@lifeforge/TedMeadow--lang-ru": "workspace:*",
|
||||
"@lifeforge/lifeforge--achievements": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ function ErrorScreen({
|
||||
errorMessage: string
|
||||
}) {
|
||||
return (
|
||||
<Stack align="center" gap="lg">
|
||||
<Stack align="center" gap="lg" height="100dvh">
|
||||
<Flex
|
||||
centered
|
||||
height="5em"
|
||||
@@ -26,7 +26,7 @@ function ErrorScreen({
|
||||
}}
|
||||
width="5em"
|
||||
>
|
||||
<Icon color="dangerous" icon="tabler:alert-triangle" size="2.25em" />
|
||||
<Icon color="dangerous" icon="tabler:alert-circle" size="2.25em" />
|
||||
</Flex>
|
||||
<Text size="xl" weight="medium">
|
||||
{errorMessage}
|
||||
|
||||
@@ -54,11 +54,9 @@ function useQRLoginApproval(scannedData: string) {
|
||||
if (!sessionData) return
|
||||
|
||||
try {
|
||||
const response = await forgeAPI
|
||||
.user.qrLogin.approveQRLogin
|
||||
.mutate({
|
||||
sessionId: sessionData.sessionId
|
||||
} as never)
|
||||
const response = await forgeAPI.user.qrLogin.approveQRLogin.mutate({
|
||||
sessionId: sessionData.sessionId
|
||||
} as never)
|
||||
|
||||
setBrowserInfo(response.browserInfo)
|
||||
setStep('success')
|
||||
|
||||
@@ -69,79 +69,83 @@ function DashboardGrid({
|
||||
}
|
||||
|
||||
return (
|
||||
<ResponsiveGridLayout
|
||||
autoSize
|
||||
className={canLayoutChange ? 'pb-64' : undefined}
|
||||
cols={{
|
||||
lg: 8,
|
||||
md: 8,
|
||||
sm: 4,
|
||||
xs: 4,
|
||||
xxs: 4
|
||||
}}
|
||||
containerPadding={[0, 0]}
|
||||
isDraggable={canLayoutChange}
|
||||
isDroppable={canLayoutChange}
|
||||
isResizable={canLayoutChange}
|
||||
layouts={enabledWidgets}
|
||||
margin={[10, 10]}
|
||||
rowHeight={100}
|
||||
width={width}
|
||||
onLayoutChange={(_, layouts) => {
|
||||
changeDashboardLayout(layouts as never)
|
||||
}}
|
||||
<Box
|
||||
asChild
|
||||
style={canLayoutChange ? { paddingBottom: '16em' } : undefined}
|
||||
>
|
||||
{[
|
||||
...new Set(
|
||||
Object.values(enabledWidgets)
|
||||
.map(widgetArray => widgetArray.map(widget => widget.i))
|
||||
.flat()
|
||||
)
|
||||
].map(widgetId => (
|
||||
<div
|
||||
key={widgetId}
|
||||
className={clsx('relative', canLayoutChange && 'cursor-move')}
|
||||
>
|
||||
{(() => {
|
||||
if (!width || !height) {
|
||||
return null
|
||||
}
|
||||
<ResponsiveGridLayout
|
||||
autoSize
|
||||
cols={{
|
||||
lg: 8,
|
||||
md: 8,
|
||||
sm: 4,
|
||||
xs: 4,
|
||||
xxs: 4
|
||||
}}
|
||||
containerPadding={[0, 0]}
|
||||
isDraggable={canLayoutChange}
|
||||
isDroppable={canLayoutChange}
|
||||
isResizable={canLayoutChange}
|
||||
layouts={enabledWidgets}
|
||||
margin={[10, 10]}
|
||||
rowHeight={100}
|
||||
width={width}
|
||||
onLayoutChange={(_, layouts) => {
|
||||
changeDashboardLayout(layouts as never)
|
||||
}}
|
||||
>
|
||||
{[
|
||||
...new Set(
|
||||
Object.values(enabledWidgets)
|
||||
.map(widgetArray => widgetArray.map(widget => widget.i))
|
||||
.flat()
|
||||
)
|
||||
].map(widgetId => (
|
||||
<div
|
||||
key={widgetId}
|
||||
className={clsx('relative', canLayoutChange && 'cursor-move')}
|
||||
>
|
||||
{(() => {
|
||||
if (!width || !height) {
|
||||
return null
|
||||
}
|
||||
|
||||
const Component = (COMPONENTS[
|
||||
widgetId as keyof typeof COMPONENTS
|
||||
] ?? NotFoundWidget) as React.FC<{
|
||||
dimension: { w: number; h: number }
|
||||
widgetId?: string
|
||||
}>
|
||||
const Component = (COMPONENTS[
|
||||
widgetId as keyof typeof COMPONENTS
|
||||
] ?? NotFoundWidget) as React.FC<{
|
||||
dimension: { w: number; h: number }
|
||||
widgetId?: string
|
||||
}>
|
||||
|
||||
const dimension = (
|
||||
enabledWidgets[getBreakpointFromWidth(width)] || []
|
||||
).find(l => l.i === widgetId)
|
||||
const dimension = (
|
||||
enabledWidgets[getBreakpointFromWidth(width)] || []
|
||||
).find(l => l.i === widgetId)
|
||||
|
||||
return (
|
||||
<Component
|
||||
dimension={{
|
||||
w: dimension?.w ?? 0,
|
||||
h: dimension?.h ?? 0
|
||||
}}
|
||||
widgetId={widgetId}
|
||||
/>
|
||||
)
|
||||
})()}
|
||||
{canLayoutChange && (
|
||||
<>
|
||||
<div className="bg-bg-900/30 absolute inset-0 top-0 left-0 rounded" />
|
||||
<Box bottom="0" position="absolute" right="0">
|
||||
<Icon
|
||||
className="1.5em"
|
||||
icon="clarity:drag-handle-corner-line"
|
||||
return (
|
||||
<Component
|
||||
dimension={{
|
||||
w: dimension?.w ?? 0,
|
||||
h: dimension?.h ?? 0
|
||||
}}
|
||||
widgetId={widgetId}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</ResponsiveGridLayout>
|
||||
)
|
||||
})()}
|
||||
{canLayoutChange && (
|
||||
<>
|
||||
<div className="bg-bg-900/30 absolute inset-0 top-0 left-0 rounded" />
|
||||
<Box bottom="0" position="absolute" right="0">
|
||||
<Icon
|
||||
className="1.5em"
|
||||
icon="clarity:drag-handle-corner-line"
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</ResponsiveGridLayout>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
43
client/src/core/dashboard/components/SaveButtonPopup.tsx
Normal file
43
client/src/core/dashboard/components/SaveButtonPopup.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react'
|
||||
|
||||
import { useMainSidebarState } from '@lifeforge/shared'
|
||||
import { Button, Flex, Text } from '@lifeforge/ui'
|
||||
|
||||
function SaveButtonPopup({
|
||||
canChange,
|
||||
setCanChange
|
||||
}: {
|
||||
canChange: boolean
|
||||
setCanChange: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}) {
|
||||
const { sidebarExpanded } = useMainSidebarState()
|
||||
|
||||
if (!canChange) return null
|
||||
|
||||
return (
|
||||
<Flex
|
||||
centered
|
||||
shadow
|
||||
bg={{ base: 'bg-100', dark: 'bg-900' }}
|
||||
bottom="1.5em"
|
||||
gap="md"
|
||||
left="50%"
|
||||
p="md"
|
||||
position="absolute"
|
||||
r="lg"
|
||||
style={{
|
||||
transform: 'translateX(-50%)'
|
||||
}}
|
||||
zIndex="50"
|
||||
>
|
||||
<Text as="p" className="font-medium" weight="medium">
|
||||
You are editing dashboard layout
|
||||
</Text>
|
||||
<Button icon="tabler:device-floppy" onClick={() => setCanChange(false)}>
|
||||
Save
|
||||
</Button>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export default SaveButtonPopup
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
|
||||
import {
|
||||
Button,
|
||||
ContextMenuItem,
|
||||
Flex,
|
||||
ModuleHeader,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
} from '@lifeforge/ui'
|
||||
|
||||
import DashboardGrid from './components/DashboardGrid'
|
||||
import SaveButtonPopup from './components/SaveButtonPopup'
|
||||
import './index.css'
|
||||
import ManageWidgetsModal from './modals/ManageWidgetsModal'
|
||||
import WidgetProvider, { useWidgets } from './providers/WidgetProvider'
|
||||
@@ -56,25 +56,10 @@ function DashboardContent() {
|
||||
/>
|
||||
|
||||
{/* Save Button Popup */}
|
||||
{canLayoutChange && (
|
||||
<div className="fixed right-6 bottom-6 z-50">
|
||||
<div className="bg-bg-100 dark:bg-bg-800 flex items-center gap-4 rounded-md p-4 shadow-lg">
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-medium">
|
||||
You are Editing Dashboard Layout
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
icon="tabler:device-floppy"
|
||||
onClick={() => setCanLayoutChange(false)}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<SaveButtonPopup
|
||||
canChange={canLayoutChange}
|
||||
setCanChange={setCanLayoutChange}
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,16 @@ import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { type IDashboardLayout, usePersonalization } from '@lifeforge/shared'
|
||||
import { Icon, Switch } from '@lifeforge/ui'
|
||||
import {
|
||||
COLORS,
|
||||
Card,
|
||||
Flex,
|
||||
Icon,
|
||||
Switch,
|
||||
Text,
|
||||
Transition,
|
||||
withOpacity
|
||||
} from '@lifeforge/ui'
|
||||
|
||||
import { useUserPersonalization } from '@/providers/features/UserPersonalizationProvider'
|
||||
|
||||
@@ -106,21 +115,49 @@ function ComponentListItem({
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
className="flex-between bg-bg-50 shadow-custom dark:bg-bg-800/50 flex gap-8 rounded-lg p-4"
|
||||
<Card
|
||||
shadow
|
||||
align="center"
|
||||
as="li"
|
||||
bg={{
|
||||
base: 'bg-50',
|
||||
dark: 'bg-800'
|
||||
}}
|
||||
direction="row"
|
||||
gap="xl"
|
||||
justify="between"
|
||||
onClick={toggleComponent}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={clsx(
|
||||
'flex size-10 shrink-0 items-center justify-center rounded-lg transition-all',
|
||||
Object.keys(enabledWidgets).includes(id)
|
||||
? 'bg-custom-500/20 text-custom-500'
|
||||
: 'bg-bg-200 text-bg-400 dark:bg-bg-700/50 dark:text-bg-500'
|
||||
)}
|
||||
>
|
||||
<Icon icon={icon} size="1.5em" />
|
||||
</div>
|
||||
<Flex align="center" gap="md">
|
||||
<Transition>
|
||||
<Flex
|
||||
centered
|
||||
className={clsx(
|
||||
Object.keys(enabledWidgets).includes(id)
|
||||
? 'bg-custom-500/20'
|
||||
: 'bg-bg-200 dark:bg-bg-700/50'
|
||||
)}
|
||||
flexShrink="0"
|
||||
height="2.5em"
|
||||
r="lg"
|
||||
style={{
|
||||
backgroundColor: Object.keys(enabledWidgets).includes(id)
|
||||
? withOpacity(COLORS['custom-500'], 0.2)
|
||||
: ''
|
||||
}}
|
||||
width="2.5em"
|
||||
>
|
||||
<Icon
|
||||
color={
|
||||
Object.keys(enabledWidgets).includes(id)
|
||||
? 'custom-500'
|
||||
: 'bg-500'
|
||||
}
|
||||
icon={icon}
|
||||
size="1.5em"
|
||||
/>
|
||||
</Flex>
|
||||
</Transition>
|
||||
<div className="flex flex-col">
|
||||
<div className="font-semibold">
|
||||
{t([
|
||||
@@ -131,21 +168,21 @@ function ComponentListItem({
|
||||
id
|
||||
])}
|
||||
</div>
|
||||
<div className="text-bg-500 text-sm">
|
||||
<Text color="muted" size="sm">
|
||||
{t([
|
||||
`widgets.${namespace}.${id}.description`,
|
||||
`widgets.${id}.description`
|
||||
])}
|
||||
</div>
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</Flex>
|
||||
<Switch
|
||||
value={isEnabled}
|
||||
onChange={() => {
|
||||
toggleComponent()
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ModalHeader } from '@lifeforge/ui'
|
||||
import { Box, ModalHeader, Stack } from '@lifeforge/ui'
|
||||
|
||||
import type { WidgetEntry } from '../../providers/WidgetProvider'
|
||||
import ComponentListItem from './components/ComponentItem'
|
||||
@@ -11,14 +11,14 @@ function ManageWidgetsModal({
|
||||
data: { widgets: Record<string, WidgetEntry> }
|
||||
}) {
|
||||
return (
|
||||
<div className="min-w-[40vw]">
|
||||
<Box minWidth="40vw">
|
||||
<ModalHeader
|
||||
icon="tabler:apps"
|
||||
namespace="common.dashboard"
|
||||
title="Manage Widgets"
|
||||
onClose={onClose}
|
||||
/>
|
||||
<ul className="space-y-2 overflow-y-auto">
|
||||
<Stack as="ul">
|
||||
{Object.entries(widgets).map(
|
||||
([key, { icon, minW, minH, maxW, maxH, namespace }]) => (
|
||||
<ComponentListItem
|
||||
@@ -33,8 +33,8 @@ function ManageWidgetsModal({
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</Stack>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,11 +60,9 @@ function BgImageSelector() {
|
||||
|
||||
async function onSubmit(file: string | File) {
|
||||
try {
|
||||
const data = await forgeAPI
|
||||
.user.personalization.updateBgImage
|
||||
.mutate({
|
||||
file
|
||||
})
|
||||
const data = await forgeAPI.user.personalization.updateBgImage.mutate({
|
||||
file
|
||||
})
|
||||
|
||||
setBgImage(forgeAPI.getMedia(data))
|
||||
toast.success('Background image updated')
|
||||
|
||||
@@ -29,18 +29,16 @@ function FontListItem({
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const togglePinMutation = useMutation(
|
||||
forgeAPI
|
||||
.user.personalization.toggleGoogleFontsPin
|
||||
.mutationOptions({
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['user', 'personalization', 'listGoogleFontsPin']
|
||||
})
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Failed to toggle font pin')
|
||||
}
|
||||
})
|
||||
forgeAPI.user.personalization.toggleGoogleFontsPin.mutationOptions({
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['user', 'personalization', 'listGoogleFontsPin']
|
||||
})
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Failed to toggle font pin')
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const [loadingPin, handleTogglePin] = usePromiseLoading(async () => {
|
||||
|
||||
@@ -30,7 +30,6 @@ export default function CoreFederationProvider({
|
||||
try {
|
||||
const result = await loadModules()
|
||||
|
||||
|
||||
setModules(result.routes)
|
||||
setGlobalProviders(result.globalProviders)
|
||||
setCategoryTranslations(result.categoryTranslations)
|
||||
|
||||
@@ -4,24 +4,39 @@ import { initReactI18next } from 'react-i18next'
|
||||
|
||||
import forgeAPI from './forgeAPI'
|
||||
|
||||
const AVAILABLE_LANG = (await fetch(
|
||||
`${import.meta.env.VITE_API_HOST}/locales/listLanguages`
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then(data => data.data)
|
||||
.catch(err => {
|
||||
console.warn('Failed to fetch available languages from backend:', err)
|
||||
return [{ name: 'en', icon: 'twemoji:flag-united-states' }]
|
||||
})) as {
|
||||
export let AVAILABLE_LANG: {
|
||||
name: string
|
||||
alternative?: string[]
|
||||
icon: string
|
||||
}[]
|
||||
}[] = [{ name: 'en', icon: 'circle-flags:gb' }]
|
||||
|
||||
i18n
|
||||
.use(I18NextHttpBackend)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
i18n.use(I18NextHttpBackend).use(initReactI18next)
|
||||
|
||||
export async function initI18n() {
|
||||
if (i18n.isInitialized) {
|
||||
return i18n
|
||||
}
|
||||
|
||||
try {
|
||||
const langRes = await fetch(
|
||||
`${import.meta.env.VITE_API_HOST}/locales/listLanguages`
|
||||
)
|
||||
|
||||
if (langRes.ok) {
|
||||
const data = await langRes.json()
|
||||
|
||||
if (data?.data) {
|
||||
AVAILABLE_LANG = data.data
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'Failed to fetch available languages, falling back to default:',
|
||||
err
|
||||
)
|
||||
}
|
||||
|
||||
await i18n.init({
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
cache: {
|
||||
@@ -82,8 +97,6 @@ i18n
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to initialize i18n: ', err)
|
||||
})
|
||||
|
||||
export default i18n
|
||||
return i18n
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import './index.css'
|
||||
|
||||
import('./bootstrap')
|
||||
|
||||
37
client/src/providers/features/I18nInitProvider.tsx
Normal file
37
client/src/providers/features/I18nInitProvider.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { ErrorScreen, LoadingScreen } from '@lifeforge/ui'
|
||||
|
||||
import { initI18n } from '@/i18n'
|
||||
|
||||
export default function I18nInitProvider({
|
||||
children
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const [initialized, setInitialized] = useState<boolean | 'error'>(false)
|
||||
|
||||
useEffect(() => {
|
||||
initI18n()
|
||||
.then(() => {
|
||||
setInitialized(true)
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to initialize i18n:', err)
|
||||
|
||||
setInitialized('error')
|
||||
})
|
||||
}, [])
|
||||
|
||||
if (!initialized) {
|
||||
return <LoadingScreen message="Initializing localization..." />
|
||||
}
|
||||
|
||||
if (initialized === 'error') {
|
||||
return (
|
||||
<ErrorScreen message="Failed to initialized localization service. Please check your i18n config." />
|
||||
)
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
@@ -24,11 +24,9 @@ async function syncUserData(
|
||||
setUserData: React.Dispatch<React.SetStateAction<any>>
|
||||
) {
|
||||
try {
|
||||
await forgeAPI
|
||||
.user.personalization.updatePersonalization
|
||||
.mutate({
|
||||
data
|
||||
})
|
||||
await forgeAPI.user.personalization.updatePersonalization.mutate({
|
||||
data
|
||||
})
|
||||
|
||||
if (setUserData) {
|
||||
setUserData((oldData: any) => {
|
||||
|
||||
@@ -26,6 +26,7 @@ import forgeAPI from '@/forgeAPI'
|
||||
import AppRoutesProvider from '@/routes/providers/AppRoutesProvider'
|
||||
|
||||
import ExternalModuleProviders from './features/ExternalModuleProviders'
|
||||
import I18nInitProvider from './features/I18nInitProvider'
|
||||
import UserPersonalizationProvider from './features/UserPersonalizationProvider'
|
||||
import { constructComponentTree, defineProviders } from './utils/providerUtils'
|
||||
|
||||
@@ -47,10 +48,6 @@ function Providers() {
|
||||
// Provider that tells components the API endpoint to use
|
||||
[APIEndpointProvider, { endpoint: import.meta.env.VITE_API_HOST }],
|
||||
|
||||
// Provider that initializes end-to-end encryption (fetches server public key)
|
||||
[EncryptionProvider, { apiHost: import.meta.env.VITE_API_HOST }],
|
||||
[EncryptionWrapper],
|
||||
|
||||
// Provider that stores all the theming information
|
||||
[PersonalizationProvider, { forgeAPI }],
|
||||
|
||||
@@ -66,6 +63,13 @@ function Providers() {
|
||||
],
|
||||
[APIOnlineStatusWrapper],
|
||||
|
||||
// All subsequent providers are gated behind the API status check!
|
||||
[I18nInitProvider],
|
||||
|
||||
// Provider that initializes end-to-end encryption (fetches server public key)
|
||||
[EncryptionProvider, { apiHost: import.meta.env.VITE_API_HOST }],
|
||||
[EncryptionWrapper],
|
||||
|
||||
// Provider that handles authentication, very obviously
|
||||
[
|
||||
AuthProvider,
|
||||
|
||||
@@ -2,17 +2,10 @@
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable",
|
||||
"ES2024.Object"
|
||||
],
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable", "ES2024.Object"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"types": ["node"],
|
||||
/* Bundler mode */
|
||||
"outDir": "${configDir}/tsbuild",
|
||||
"moduleResolution": "bundler",
|
||||
@@ -29,15 +22,9 @@
|
||||
"rootDir": "../",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@core/*": [
|
||||
"../core/*"
|
||||
],
|
||||
"@functions/*": [
|
||||
"../server/src/core/functions/*"
|
||||
],
|
||||
"@/*": ["./src/*"],
|
||||
"@core/*": ["../core/*"],
|
||||
"@functions/*": ["../server/src/core/functions/*"]
|
||||
}
|
||||
},
|
||||
"references": [
|
||||
@@ -45,7 +32,5 @@
|
||||
"path": "../server"
|
||||
}
|
||||
],
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
]
|
||||
}
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"empty": {
|
||||
"welcome": {
|
||||
"title": "Welcome to LifeForge!",
|
||||
"description": "Get started by installing some modules from the ForgeStore."
|
||||
"description": "Get started by installing some modules from Forgistry."
|
||||
},
|
||||
"notFound": {
|
||||
"title": "Widget Not Found",
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useState } from 'react'
|
||||
|
||||
import { Button } from '@/components/inputs'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
|
||||
import { TAILWIND_PALETTE } from '@/system'
|
||||
|
||||
import { TagsFilter } from './index'
|
||||
|
||||
@@ -79,10 +79,10 @@ export function VirtualGrid<T>({
|
||||
>
|
||||
<Grid
|
||||
as="div"
|
||||
templateCols={`repeat(${itemsPerRow}, 1fr)`}
|
||||
gap="md"
|
||||
pb="md"
|
||||
style={style}
|
||||
templateCols={`repeat(${itemsPerRow}, 1fr)`}
|
||||
width="100%"
|
||||
>
|
||||
{items.slice(fromIndex, toIndex).map(item => (
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
|
||||
import { Button, Listbox, ListboxOption } from '@/components/inputs'
|
||||
import { Box, Grid, Text } from '@/components/primitives'
|
||||
|
||||
import { ScrollableStory } from '@/storybook/ScrollableStory'
|
||||
import { TAILWIND_PALETTE } from '@/system'
|
||||
|
||||
@@ -25,7 +24,7 @@ export const Default: Story = {
|
||||
title: 'Cool Widget'
|
||||
},
|
||||
render: args => (
|
||||
<Grid templateCols={3} gap="lg" templateRows={3}>
|
||||
<Grid gap="lg" templateCols={3} templateRows={3}>
|
||||
<Widget {...args}>
|
||||
<Text as="p" color="bg-600">
|
||||
This is a dashboard item.
|
||||
@@ -63,7 +62,7 @@ export const WithDescription: Story = {
|
||||
title: 'Cool Widget'
|
||||
},
|
||||
render: args => (
|
||||
<Grid templateCols={3} gap="lg" templateRows={3}>
|
||||
<Grid gap="lg" templateCols={3} templateRows={3}>
|
||||
<Widget {...args}>
|
||||
<Text as="p" color="bg-600">
|
||||
This is a dashboard item.
|
||||
@@ -101,7 +100,7 @@ export const WithIconColor: Story = {
|
||||
},
|
||||
render: args => (
|
||||
<ScrollableStory>
|
||||
<Grid templateCols={3} gap="md">
|
||||
<Grid gap="md" templateCols={3}>
|
||||
{[
|
||||
TAILWIND_PALETTE.red[500],
|
||||
TAILWIND_PALETTE.blue[500],
|
||||
@@ -130,7 +129,7 @@ export const WithActionComponent: Story = {
|
||||
title: 'A Cool Widget'
|
||||
},
|
||||
render: args => (
|
||||
<Grid templateCols={2} gap="lg" templateRows={3}>
|
||||
<Grid gap="lg" templateCols={2} templateRows={3}>
|
||||
<Widget {...args}>
|
||||
<Text as="p" color="bg-600">
|
||||
This widget has a plus button beside the title. When clicked, maybe a
|
||||
@@ -204,7 +203,7 @@ export const LargeIconWithIconColor: Story = {
|
||||
},
|
||||
render: args => (
|
||||
<ScrollableStory>
|
||||
<Grid templateCols={2} gap="md">
|
||||
<Grid gap="md" templateCols={2}>
|
||||
{[
|
||||
TAILWIND_PALETTE.red[500],
|
||||
TAILWIND_PALETTE.blue[500],
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text } from '@/components/primitives'
|
||||
import { Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import * as styles from './Alert.css'
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Button } from '@/components/inputs'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
interface EmptyStateScreenProps {
|
||||
/** Props for the call-to-action button. Refer to the Button component for available props. */
|
||||
@@ -71,6 +70,7 @@ export function EmptyStateScreen({
|
||||
<Text
|
||||
align="center"
|
||||
as="h2"
|
||||
color={{ base: 'bg-800', dark: 'bg-100' }}
|
||||
size={smaller ? '2xl' : '3xl'}
|
||||
style={{ paddingLeft: '1.5rem', paddingRight: '1.5rem' }}
|
||||
weight="semibold"
|
||||
|
||||
@@ -3,8 +3,7 @@ import clsx from 'clsx'
|
||||
|
||||
import { usePersonalization } from '@lifeforge/shared'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Bordered, Flex, Text, Transition } from '@/components/primitives'
|
||||
import { Bordered, Flex, Icon, Text, Transition } from '@/components/primitives'
|
||||
|
||||
import { checkboxRootRecipe } from './Checkbox.css'
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ import { usePersonalization } from '@lifeforge/shared'
|
||||
|
||||
import { Card } from '@/components/layout'
|
||||
import { ModalHeader } from '@/components/overlays'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Grid, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Grid, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import PALETTES from './constants/palettes.json'
|
||||
|
||||
@@ -27,11 +26,11 @@ function FlatUIColorsModal({
|
||||
onClose={onClose}
|
||||
/>
|
||||
<Grid
|
||||
style={{ gap: '0.75rem' }}
|
||||
templateCols={{
|
||||
base: 1,
|
||||
sm: 'repeat(auto-fill, minmax(300px, 1fr))'
|
||||
}}
|
||||
style={{ gap: '0.75rem' }}
|
||||
>
|
||||
{PALETTES.map(({ name, icon, colors }) => (
|
||||
<Card key={name} bg={{ base: 'bg-100', dark: 'bg-800' }}>
|
||||
@@ -44,7 +43,7 @@ function FlatUIColorsModal({
|
||||
{name}
|
||||
</Text>
|
||||
</Flex>
|
||||
<Grid templateCols={5} style={{ gap: '0.5rem' }}>
|
||||
<Grid style={{ gap: '0.5rem' }} templateCols={5}>
|
||||
{colors.map((flatUiColor, index) => (
|
||||
<Flex
|
||||
key={index}
|
||||
|
||||
@@ -4,8 +4,7 @@ import { sortFn } from 'color-sorter'
|
||||
import { usePersonalization } from '@lifeforge/shared'
|
||||
|
||||
import { ModalHeader } from '@/components/overlays'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Grid } from '@/components/primitives'
|
||||
import { Box, Flex, Grid, Icon } from '@/components/primitives'
|
||||
|
||||
import * as styles from './MorandiColorPaletteModal.css'
|
||||
import { MORANDI_COLORS } from './constants/morandi_colors'
|
||||
@@ -30,10 +29,10 @@ function MorandiColorPaletteModal({
|
||||
onClose={onClose}
|
||||
/>
|
||||
<Grid
|
||||
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
|
||||
pb="md"
|
||||
px="md"
|
||||
style={{ gap: '0.75rem' }}
|
||||
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
|
||||
>
|
||||
{MORANDI_COLORS.sort(sortFn).map((morandiColor, index) => (
|
||||
<Flex key={index} asChild align="center" justify="center" r="md">
|
||||
|
||||
@@ -4,8 +4,7 @@ import { memo, useMemo } from 'react'
|
||||
|
||||
import { usePersonalization } from '@lifeforge/shared'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import * as styles from './ColorItem.css'
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ModalHeader } from '@/components/overlays'
|
||||
import { Box, Flex, Grid, Text } from '@/components/primitives'
|
||||
|
||||
import { TAILWIND_PALETTE } from '@/system'
|
||||
|
||||
import * as styles from './TailwindCSSColorsModal.css'
|
||||
@@ -53,8 +52,8 @@ function TailwindCSSColorsModal({
|
||||
<Grid
|
||||
key={index}
|
||||
as="ul"
|
||||
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
|
||||
style={{ gap: '0.75rem' }}
|
||||
templateCols="repeat(auto-fit, minmax(4rem, 1fr))"
|
||||
width="100%"
|
||||
>
|
||||
{Object.entries(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ComboboxOption as HeadlessComboboxOption } from '@headlessui/react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import {
|
||||
Bordered,
|
||||
Box,
|
||||
Flex,
|
||||
Icon,
|
||||
Text,
|
||||
Transition,
|
||||
WithDivide
|
||||
|
||||
@@ -4,8 +4,7 @@ import {
|
||||
} from '@headlessui/react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import { InputActionButton } from '../shared/components/InputActionButton'
|
||||
import { InputIcon } from '../shared/components/InputIcon'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { TextInput } from '@/components/inputs'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Text } from '@/components/primitives'
|
||||
import { Box, Icon, Text } from '@/components/primitives'
|
||||
|
||||
export function ImageURL({
|
||||
file,
|
||||
|
||||
@@ -4,8 +4,7 @@ import type { DropzoneInputProps, DropzoneRootProps } from 'react-dropzone'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Button } from '@/components/inputs'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text } from '@/components/primitives'
|
||||
import { Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import * as styles from './DnDContainer.css'
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { Button } from '@/components/inputs'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import { FILE_ICONS } from '../../../constants/file_icons'
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ListboxInput, ListboxOption } from '@/components/inputs'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Text } from '@/components/primitives'
|
||||
import { Icon, Text } from '@/components/primitives'
|
||||
|
||||
import {
|
||||
type IPixabaySearchFilter,
|
||||
@@ -26,9 +25,9 @@ export function CategoryFilter({
|
||||
style={{ height: '1.25rem', width: '1.25rem' }}
|
||||
/>
|
||||
<Text
|
||||
truncate
|
||||
as="span"
|
||||
style={{ display: 'block', marginTop: '-1px' }}
|
||||
truncate
|
||||
>
|
||||
{CATEGORIES.find(l => l.id === category)?.name ?? 'None'}
|
||||
</Text>
|
||||
|
||||
@@ -30,9 +30,9 @@ export function ColorFilter({ colors, updateFilters }: ColorFilterProps) {
|
||||
<div />
|
||||
</Bordered>
|
||||
<Text
|
||||
truncate
|
||||
as="span"
|
||||
style={{ display: 'block', marginTop: '-1px' }}
|
||||
truncate
|
||||
>
|
||||
{COLORS.find(l => l.id === colors)?.name ?? 'None'}
|
||||
</Text>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Switch } from '@/components/inputs'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text } from '@/components/primitives'
|
||||
import { Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import { type PixabaySearchFilterAction } from '../../../typescript/pixabay_interfaces'
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { ListboxInput, ListboxOption } from '@/components/inputs'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Text } from '@/components/primitives'
|
||||
import { Icon, Text } from '@/components/primitives'
|
||||
|
||||
import {
|
||||
type IPixabaySearchFilter,
|
||||
@@ -30,9 +29,9 @@ export function ImageTypeFilter({
|
||||
style={{ height: '1.25rem', width: '1.25rem' }}
|
||||
/>
|
||||
<Text
|
||||
truncate
|
||||
as="span"
|
||||
style={{ display: 'block', marginTop: '-1px' }}
|
||||
truncate
|
||||
>
|
||||
{t(
|
||||
`imagePicker.imageType.${IMAGE_TYPES.find(l => l.id === imageType)?.id}`
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
import { Button, SearchInput } from '@/components/inputs'
|
||||
import { Flex } from '@/components/primitives'
|
||||
import { WithQueryData } from '@/components/utilities'
|
||||
|
||||
import { forgeAPI } from '@/utils/forgeAPI'
|
||||
|
||||
import { SearchFilterModal } from './components/SearchFilterModal'
|
||||
|
||||
@@ -3,8 +3,7 @@ import { useCallback, useMemo, useState } from 'react'
|
||||
import { usePersonalization } from '@lifeforge/shared'
|
||||
|
||||
import { TagChip } from '@/components/data-display'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text, Transition } from '@/components/primitives'
|
||||
import { Flex, Icon, Text, Transition } from '@/components/primitives'
|
||||
|
||||
export function ChipSelector({
|
||||
options,
|
||||
|
||||
@@ -2,8 +2,7 @@ import { useCallback, useState } from 'react'
|
||||
|
||||
import { GoBackButton } from '@/components/navigation'
|
||||
import { ModalHeader } from '@/components/overlays'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text, Transition } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text, Transition } from '@/components/primitives'
|
||||
|
||||
import { IconSet } from './pages/IconSet'
|
||||
import { IconSetList } from './pages/IconSetList/index'
|
||||
|
||||
@@ -40,11 +40,11 @@ function _CategoryEntry({
|
||||
</Text>
|
||||
</Box>
|
||||
<Grid
|
||||
gap="sm"
|
||||
templateCols={{
|
||||
base: '1',
|
||||
sm: 'repeat(auto-fill, minmax(320px, 1fr))'
|
||||
}}
|
||||
gap="sm"
|
||||
>
|
||||
{iconSets.map(iconSet => (
|
||||
<IconSetEntry
|
||||
|
||||
@@ -3,7 +3,12 @@ import { collections as importedCollections } from '@iconify/collections'
|
||||
import { type IconifyInfo } from '@iconify/types'
|
||||
import { memo, useCallback, useMemo } from 'react'
|
||||
|
||||
import { Button, Listbox, ListboxOption, SearchInput } from '@/components/inputs'
|
||||
import {
|
||||
Button,
|
||||
Listbox,
|
||||
ListboxOption,
|
||||
SearchInput
|
||||
} from '@/components/inputs'
|
||||
import { Box, Flex } from '@/components/primitives'
|
||||
|
||||
const collections: Record<string, IconifyInfo> = importedCollections
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import { COLORS, TAILWIND_PALETTE } from '@/system'
|
||||
|
||||
import { ListboxNullOption } from './components/ListboxNullOption'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ListboxOption as HeadlessListboxOption } from '@headlessui/react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Icon,
|
||||
Text,
|
||||
Transition,
|
||||
WithDivide
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ListboxOption as HeadlessListboxOption } from '@headlessui/react'
|
||||
import { formatHex, parse } from 'culori'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import {
|
||||
Bordered,
|
||||
Box,
|
||||
Flex,
|
||||
Icon,
|
||||
Text,
|
||||
Transition,
|
||||
WithDivide
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useAPIEndpoint } from '@lifeforge/shared'
|
||||
import { ComboboxInput, ComboboxOption } from '@/components/inputs'
|
||||
import type { InputVariants } from '@/components/inputs/shared/types'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import { forgeAPI } from '@/utils/forgeAPI'
|
||||
|
||||
import { useInputLabel } from '../shared/hooks/useInputLabel'
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Text } from '@/components/primitives'
|
||||
|
||||
import { ScrollableStory } from '@/storybook/ScrollableStory'
|
||||
|
||||
import { RRuleInput } from './index'
|
||||
|
||||
@@ -2,8 +2,7 @@ import type { StoryObj, Meta as _Meta } from '@storybook/react-vite'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
import { WithQuery } from '@/components/utilities'
|
||||
|
||||
import { SearchInput } from './index'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useInputLabel } from '@/components/inputs/shared/hooks/useInputLabel'
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text } from '@/components/primitives'
|
||||
import { Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
export function SliderHeader({
|
||||
icon,
|
||||
|
||||
@@ -57,13 +57,13 @@ export function SliderInput({
|
||||
>
|
||||
<Box className={styles.fill} style={{ width: `${progress}%` }} />
|
||||
<input
|
||||
className={styles.input}
|
||||
disabled={disabled}
|
||||
max={max}
|
||||
min={min}
|
||||
step={step}
|
||||
type="range"
|
||||
value={value}
|
||||
className={styles.input}
|
||||
onChange={e => {
|
||||
onChange(parseFloat(e.target.value))
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { type ComponentPropsWithoutRef, type ReactNode } from 'react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text, Transition } from '@/components/primitives'
|
||||
import { Flex, Icon, Text, Transition } from '@/components/primitives'
|
||||
|
||||
import type { InputVariant } from '../types'
|
||||
|
||||
@@ -42,8 +41,8 @@ export function InputActionButton({
|
||||
justify="center"
|
||||
p="sm"
|
||||
position="absolute"
|
||||
right={hasError ? (variant === 'classic' ? '3em' : '2.5em') : '0'}
|
||||
r="lg"
|
||||
right={hasError ? (variant === 'classic' ? '3em' : '2.5em') : '0'}
|
||||
style={{
|
||||
// the `mr` props cannot be used since the 0.75rem value is required to be exact for the plain variant
|
||||
marginRight: variant === 'classic' ? '1em' : '0.75em',
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import clsx from 'clsx'
|
||||
import { type CSSProperties, memo } from 'react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text, Transition } from '@/components/primitives'
|
||||
import { Flex, Icon, Text, Transition } from '@/components/primitives'
|
||||
|
||||
import { useInputFocused } from '../../contexts/InputFocusContext'
|
||||
import {
|
||||
|
||||
@@ -36,9 +36,9 @@ export function InputFocusProvider({
|
||||
return (
|
||||
<span
|
||||
ref={containerRef as React.Ref<HTMLSpanElement>}
|
||||
style={{ display: 'contents' }}
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
style={{ display: 'contents' }}
|
||||
>
|
||||
<InputFocusContext.Provider value={isFocused}>
|
||||
{children}
|
||||
|
||||
@@ -66,9 +66,9 @@ export const Default: Story = {
|
||||
<ModuleHeader />
|
||||
|
||||
<Grid
|
||||
templateCols="repeat(auto-fit, minmax(200px, 1fr))"
|
||||
gap="md"
|
||||
mb="xl"
|
||||
templateCols="repeat(auto-fit, minmax(200px, 1fr))"
|
||||
width="100%"
|
||||
>
|
||||
<StatCard label="Total Items" value="1,284" />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Text } from '@/components/primitives'
|
||||
import { Icon, Text } from '@/components/primitives'
|
||||
|
||||
export function EllipsisIcon() {
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Box } from '@/components/primitives'
|
||||
|
||||
import { TAILWIND_PALETTE } from '@/system'
|
||||
|
||||
import { Tabs } from '../Tabs'
|
||||
|
||||
@@ -4,7 +4,6 @@ import { BrowserRouter } from 'react-router'
|
||||
|
||||
import { ContextMenuItem } from '@/components/overlays'
|
||||
import { Flex, Text } from '@/components/primitives'
|
||||
|
||||
import { TAILWIND_PALETTE } from '@/system'
|
||||
|
||||
import { SidebarWrapper } from '../SidebarWrapper'
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useModuleSidebarState } from '@lifeforge/shared'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Transition } from '@/components/primitives'
|
||||
import { Box, Icon, Transition } from '@/components/primitives'
|
||||
|
||||
import * as styles from './SidebarActionButton.css'
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useModuleSidebarState } from '@lifeforge/shared'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Text } from '@/components/primitives'
|
||||
import { Box, Icon, Text } from '@/components/primitives'
|
||||
|
||||
export function SidebarCancelButton({ onClick }: { onClick: () => void }) {
|
||||
const { setIsSidebarOpen } = useModuleSidebarState()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Bordered, Text } from '@/components/primitives'
|
||||
import { Bordered, Icon, Text } from '@/components/primitives'
|
||||
|
||||
export function SidebarItemIcon({
|
||||
icon,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Text, Transition } from '@/components/primitives'
|
||||
import { Box, Icon, Text, Transition } from '@/components/primitives'
|
||||
|
||||
export function SidebarItemSubsectionExpandIcon({
|
||||
toggleSubsection,
|
||||
|
||||
@@ -3,11 +3,9 @@ import _ from 'lodash'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Link, useLocation } from '@lifeforge/shared'
|
||||
import { useMainSidebarState } from '@lifeforge/shared'
|
||||
import { Link, useLocation, useMainSidebarState } from '@lifeforge/shared'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text } from '@/components/primitives'
|
||||
import { Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import * as styles from './SidebarSubsectionItemLink.css'
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { useModuleSidebarState } from '@lifeforge/shared'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Flex, Text } from '@/components/primitives'
|
||||
import { Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import * as styles from './SidebarSubsectionItemWithOnClick.css'
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import * as styles from './ContextMenuGroup.css'
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import _ from 'lodash'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import { vars } from '@/system'
|
||||
|
||||
import * as styles from './ContextMenuItem.css'
|
||||
|
||||
@@ -291,8 +291,8 @@ export const Positioning: Story = {
|
||||
bottom="0"
|
||||
p="xs"
|
||||
position="absolute"
|
||||
right="0"
|
||||
r="md"
|
||||
right="0"
|
||||
style={{ margin: '0.5rem' }}
|
||||
>
|
||||
<PlaceholderContent
|
||||
|
||||
@@ -6,7 +6,7 @@ export function EncryptionWrapper({ children }: { children: React.ReactNode }) {
|
||||
const { ready, error } = useEncryption()
|
||||
|
||||
if (!ready) {
|
||||
return <LoadingScreen message="Checking API status..." />
|
||||
return <LoadingScreen message="Initializing end-to-end encryption..." />
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
|
||||
import { Icon } from '@/components/primitives'
|
||||
import { Box, Flex, Text } from '@/components/primitives'
|
||||
import { Box, Flex, Icon, Text } from '@/components/primitives'
|
||||
|
||||
import { Tooltip } from './index'
|
||||
|
||||
|
||||
@@ -29,7 +29,11 @@ const RESPONSIVE_PROPS = [
|
||||
{ className: 'lf-gtr', property: 'gridTemplateRows', customProp: '--lf-gtr' },
|
||||
{ className: 'lf-zi', property: 'zIndex', customProp: '--lf-zi' },
|
||||
{ className: 'lf-rw', property: '--lf-ring-width', customProp: '--lf-rw' },
|
||||
{ className: 'lf-row', property: '--lf-ring-offset-width', customProp: '--lf-row' }
|
||||
{
|
||||
className: 'lf-row',
|
||||
property: '--lf-ring-offset-width',
|
||||
customProp: '--lf-row'
|
||||
}
|
||||
] as const
|
||||
|
||||
// Breakpoint media queries
|
||||
|
||||
@@ -4,18 +4,10 @@
|
||||
"jsx": "react-jsx",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"paths": {
|
||||
"@/components/*": [
|
||||
"./src/components/*"
|
||||
],
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
"@/components/*": ["./src/components/*"],
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
@@ -28,8 +20,5 @@
|
||||
"skipLibCheck": true,
|
||||
"verbatimModuleSyntax": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*",
|
||||
"./.storybook/*"
|
||||
]
|
||||
}
|
||||
"include": ["./src/**/*", "./.storybook/*"]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
import { CleanedSchemas, IPBService } from '@lifeforge/server-utils'
|
||||
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
|
||||
import create from './services/create'
|
||||
import deleteRecord from './services/delete'
|
||||
import getFirstListItem from './services/getFirstListItem'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
import chalk from 'chalk'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
@@ -11,8 +12,6 @@ import {
|
||||
ICreateFactory
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
|
||||
import { PBLogger } from '..'
|
||||
import getFinalCollectionName from '../utils/getFinalCollectionName'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
import chalk from 'chalk'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
@@ -9,8 +10,6 @@ import {
|
||||
IDeleteFactory
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
|
||||
import { PBLogger } from '..'
|
||||
import getFinalCollectionName from '../utils/getFinalCollectionName'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
import chalk from 'chalk'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
@@ -12,8 +13,6 @@ import {
|
||||
IGetFirstListItemFactory
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
|
||||
import { PBLogger } from '..'
|
||||
import getFinalCollectionName from '../utils/getFinalCollectionName'
|
||||
import { recursivelyBuildFilter } from '../utils/recursivelyConstructFilter'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
import chalk from 'chalk'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
@@ -12,8 +13,6 @@ import {
|
||||
IGetFullListFactory
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
|
||||
import { PBLogger } from '..'
|
||||
import getFinalCollectionName from '../utils/getFinalCollectionName'
|
||||
import { recursivelyBuildFilter } from '../utils/recursivelyConstructFilter'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
import chalk from 'chalk'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
@@ -13,8 +14,6 @@ import {
|
||||
IGetListReturnType
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
|
||||
import { PBLogger } from '..'
|
||||
import getFinalCollectionName from '../utils/getFinalCollectionName'
|
||||
import { recursivelyBuildFilter } from '../utils/recursivelyConstructFilter'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
import chalk from 'chalk'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
@@ -10,8 +11,6 @@ import {
|
||||
IGetOneFactory
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
|
||||
import { PBLogger } from '..'
|
||||
import getFinalCollectionName from '../utils/getFinalCollectionName'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
import chalk from 'chalk'
|
||||
import PocketBase from 'pocketbase'
|
||||
|
||||
@@ -11,8 +12,6 @@ import {
|
||||
IUpdateFactory
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { toPocketBaseCollectionName } from '@functions/database/dbUtils'
|
||||
|
||||
import { PBLogger } from '..'
|
||||
import getFinalCollectionName from '../utils/getFinalCollectionName'
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { ROOT_DIR } from '@constants'
|
||||
import { decrypt2 } from '@functions/auth/encryption'
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
import chalk from 'chalk'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import { IPBService } from '@lifeforge/server-utils'
|
||||
|
||||
import { decrypt2 } from '@functions/auth/encryption'
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
|
||||
import PBService from './PBService'
|
||||
|
||||
const logger = createServiceLogger('API Key Vault')
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ROOT_DIR } from '@constants'
|
||||
import path from 'path'
|
||||
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
import path from 'path'
|
||||
|
||||
// Key storage paths
|
||||
export const KEYS_DIR = path.join(ROOT_DIR, 'keys')
|
||||
|
||||
9
server/src/core/functions/external/ai.ts
vendored
9
server/src/core/functions/external/ai.ts
vendored
@@ -1,3 +1,7 @@
|
||||
import { getAPIKey } from '@functions/database'
|
||||
import { validateCallerAccess } from '@functions/database/getAPIKey'
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
import { zodTextFormat } from '@functions/utils/zodResponseFormat'
|
||||
import chalk from 'chalk'
|
||||
import Groq from 'groq-sdk'
|
||||
import { ChatCompletionMessageParam as GroqChatCompletionMessageParam } from 'groq-sdk/resources/chat/completions.mjs'
|
||||
@@ -10,11 +14,6 @@ import {
|
||||
getCallerModuleId
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { getAPIKey } from '@functions/database'
|
||||
import { validateCallerAccess } from '@functions/database/getAPIKey'
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
import { zodTextFormat } from '@functions/utils/zodResponseFormat'
|
||||
|
||||
const logger = createServiceLogger('AI')
|
||||
|
||||
const fetchAI: FetchAIFunc = async ({
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ROOT_DIR } from '@constants'
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
|
||||
import { ensureKeysExist } from '@functions/encryption'
|
||||
import { coreLogger } from '@functions/logging'
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
|
||||
export default function ensureCredentials(): void {
|
||||
dotenv.config({
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ROOT_DIR } from '@constants'
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
import chalk from 'chalk'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import z from 'zod'
|
||||
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
|
||||
const localeLogger = createServiceLogger('Locale')
|
||||
|
||||
export const ALLOWED_NAMESPACE = ['apps', 'common'] as const
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ROOT_DIR } from '@constants'
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
import chalk from 'chalk'
|
||||
import fs from 'fs'
|
||||
import _ from 'lodash'
|
||||
import path from 'path'
|
||||
|
||||
import { createServiceLogger } from '@functions/logging'
|
||||
|
||||
const IS_PRODUCTION = process.env.NODE_ENV === 'production'
|
||||
|
||||
const logger = createServiceLogger('Route Loader')
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
* The main export is:
|
||||
* - `registerController`: Function to register a ForgeControllerBuilder with an Express router
|
||||
*/
|
||||
import { encryptResponse } from '@functions/encryption'
|
||||
import { coreLogger } from '@functions/logging'
|
||||
import type { Request, Response, Router } from 'express'
|
||||
|
||||
import {
|
||||
@@ -24,9 +26,6 @@ import {
|
||||
MediaConfig
|
||||
} from '@lifeforge/server-utils'
|
||||
|
||||
import { encryptResponse } from '@functions/encryption'
|
||||
import { coreLogger } from '@functions/logging'
|
||||
|
||||
import checkRecordExistence from '../utils/checkRecordExistence'
|
||||
import { createCoreContext } from '../utils/coreContext'
|
||||
import getAESKey from '../utils/getAESKey'
|
||||
@@ -89,7 +88,6 @@ function createHandler<
|
||||
|
||||
const aesKey = getAESKey(req, res, encrypted, callerModuleId)
|
||||
|
||||
|
||||
try {
|
||||
parseQuery(req, schema.query)
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { PBService, checkExistence } from '@functions/database'
|
||||
import { Request } from 'express'
|
||||
|
||||
import { ClientError } from '@lifeforge/server-utils'
|
||||
|
||||
import { PBService, checkExistence } from '@functions/database'
|
||||
|
||||
async function check(
|
||||
pb: PBService<any>,
|
||||
collection: string,
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
* This module provides the implementation of core utilities that modules
|
||||
* access via the `core` parameter in their callbacks.
|
||||
*/
|
||||
import { type Logger, createLogger } from '@lifeforge/log'
|
||||
import { CoreContext, IPBService } from '@lifeforge/server-utils'
|
||||
|
||||
import {
|
||||
decrypt,
|
||||
decrypt2,
|
||||
@@ -28,6 +25,9 @@ import {
|
||||
import { checkModulesAvailability } from '@functions/utils/checkModulesAvailability'
|
||||
import TempFileManager from '@functions/utils/tempFileManager'
|
||||
|
||||
import { type Logger, createLogger } from '@lifeforge/log'
|
||||
import { CoreContext, IPBService } from '@lifeforge/server-utils'
|
||||
|
||||
// Cache loggers per module to avoid creating new file stream listeners per request
|
||||
const loggerCache = new Map<string, Logger>()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import { decryptAESKey } from '@functions/encryption'
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import { clientError } from './response'
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { coreLogger } from '@functions/logging'
|
||||
import chalk from 'chalk'
|
||||
|
||||
import { MediaConfig } from '@lifeforge/server-utils'
|
||||
|
||||
import { coreLogger } from '@functions/logging'
|
||||
|
||||
type MediaResponse = Record<
|
||||
string,
|
||||
Express.Multer.File | Express.Multer.File[] | undefined
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { PBService } from '@functions/database'
|
||||
import { Request, Response } from 'express'
|
||||
import Pocketbase from 'pocketbase'
|
||||
|
||||
import { PBService } from '@functions/database'
|
||||
|
||||
export default async function isAuthTokenValid(
|
||||
req: Request<unknown, unknown, unknown, unknown>,
|
||||
res: Response,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { globalTaskPool } from '@functions/socketio/taskPool'
|
||||
import Pocketbase from 'pocketbase'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { globalTaskPool } from '@functions/socketio/taskPool'
|
||||
|
||||
export function setupSocket(io: Server) {
|
||||
// QR Login namespace - no authentication required
|
||||
// This allows unauthenticated desktop clients to listen for login approval
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { coreLogger } from '@functions/logging'
|
||||
import chalk from 'chalk'
|
||||
import { Request, Response } from 'express'
|
||||
import morgan from 'morgan'
|
||||
|
||||
import { coreLogger } from '@functions/logging'
|
||||
|
||||
const METHOD_COLOR = {
|
||||
GET: '#34ace0',
|
||||
POST: '#2ed573'
|
||||
|
||||
@@ -103,5 +103,4 @@ const coreRoutes = forgeRouter({
|
||||
encryptionPublicKey
|
||||
})
|
||||
|
||||
|
||||
export default coreRoutes
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { ROOT_DIR } from '@constants'
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
|
||||
import { forgeRouter } from '@lifeforge/server-utils'
|
||||
|
||||
import traceRouteStack from '@functions/initialization/traceRouteStack'
|
||||
import { loadModuleRoutes } from '@functions/modules/loadModuleRoutes'
|
||||
import { registerRoutes } from '@functions/routes/functions/forgeRouter'
|
||||
import { clientError } from '@functions/routes/utils/response'
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
|
||||
import { forgeRouter } from '@lifeforge/server-utils'
|
||||
|
||||
import coreRoutes from './core.routes'
|
||||
import forge from './forge'
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { PORT } from '@constants'
|
||||
import chalk from 'chalk'
|
||||
import { program } from 'commander'
|
||||
import fs from 'fs'
|
||||
import { createServer } from 'node:http'
|
||||
|
||||
import checkDB from '@functions/database/dbUtils'
|
||||
import ensureCredentials from '@functions/initialization/ensureCredentials'
|
||||
import { LocaleService } from '@functions/initialization/localeService'
|
||||
import traceRouteStack from '@functions/initialization/traceRouteStack'
|
||||
import { LOG_LEVELS, type LogLevel, coreLogger } from '@functions/logging'
|
||||
import createSocketServer from '@functions/socketio/createSocketServer'
|
||||
import chalk from 'chalk'
|
||||
import { program } from 'commander'
|
||||
import fs from 'fs'
|
||||
import { createServer } from 'node:http'
|
||||
|
||||
import app from './core/app'
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import dayjs from 'dayjs'
|
||||
import z from 'zod'
|
||||
|
||||
import { createForge, forgeRouter } from '@lifeforge/server-utils'
|
||||
|
||||
import {
|
||||
connectToPocketBase,
|
||||
validateEnvironmentVariables
|
||||
} from '@functions/database/dbUtils'
|
||||
import dayjs from 'dayjs'
|
||||
import z from 'zod'
|
||||
|
||||
import { createForge, forgeRouter } from '@lifeforge/server-utils'
|
||||
|
||||
const forge = createForge({}, 'backups')
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createForge, forgeRouter } from '@lifeforge/server-utils'
|
||||
|
||||
import {
|
||||
connectToPocketBase,
|
||||
validateEnvironmentVariables
|
||||
} from '@functions/database/dbUtils'
|
||||
|
||||
import { createForge, forgeRouter } from '@lifeforge/server-utils'
|
||||
|
||||
const forge = createForge({}, 'database')
|
||||
|
||||
const list = forge
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ROOT_DIR } from '@constants'
|
||||
import {
|
||||
ALLOWED_NAMESPACE,
|
||||
LocaleService
|
||||
} from '@functions/initialization/localeService'
|
||||
import chalk from 'chalk'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
@@ -7,11 +11,6 @@ import z from 'zod'
|
||||
import { ClientError, createForge, forgeRouter } from '@lifeforge/server-utils'
|
||||
import { normalizeSubnamespace } from '@lifeforge/shared'
|
||||
|
||||
import {
|
||||
ALLOWED_NAMESPACE,
|
||||
LocaleService
|
||||
} from '@functions/initialization/localeService'
|
||||
|
||||
const forge = createForge({}, 'locales')
|
||||
|
||||
// Scan apps directory for modules with locales
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user