miolo 3.0.0-beta.21 → 3.0.0-beta.210

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/bin/build/build.mjs +53 -0
  2. package/bin/build/build_bin.mjs +17 -0
  3. package/bin/build/cli/client.mjs +52 -0
  4. package/bin/build/cli/css.mjs +22 -0
  5. package/bin/build/cli/index.mjs +40 -0
  6. package/bin/build/cli/ssr.mjs +21 -0
  7. package/bin/build/server/aliases.mjs +112 -0
  8. package/bin/build/server/babel.config.js +24 -0
  9. package/bin/build/server/banner.mjs +20 -0
  10. package/bin/build/server/bundle.mjs +111 -0
  11. package/bin/build/server/fix.mjs +15 -0
  12. package/bin/build/server/index.mjs +69 -0
  13. package/bin/build/server/options.mjs +83 -0
  14. package/bin/create/auth.mjs +23 -0
  15. package/bin/create/copy.mjs +175 -0
  16. package/bin/create/docker.mjs +25 -0
  17. package/bin/create/index.mjs +137 -0
  18. package/bin/create/pkgjson.mjs +72 -0
  19. package/bin/create/prepare-template.mjs +158 -0
  20. package/bin/create/validation.mjs +27 -0
  21. package/bin/dev/dev.mjs +32 -23
  22. package/bin/dev/dev_start.mjs +6 -5
  23. package/bin/index.mjs +94 -52
  24. package/bin/prod-bin/create-bin.mjs +42 -27
  25. package/bin/prod-bin/run.mjs +13 -9
  26. package/bin/{prod-run → run}/pid.mjs +4 -4
  27. package/bin/run/restart.mjs +13 -0
  28. package/bin/run/start.mjs +18 -0
  29. package/bin/run/stop.mjs +20 -0
  30. package/bin/util.mjs +35 -11
  31. package/package.json +59 -39
  32. package/src/config/.env +34 -12
  33. package/src/config/defaults.mjs +253 -185
  34. package/src/config/env.mjs +40 -22
  35. package/src/config/index.mjs +19 -24
  36. package/src/config/util.mjs +25 -10
  37. package/src/db-conn.mjs +34 -0
  38. package/src/engines/cron/emails.mjs +10 -5
  39. package/src/engines/cron/index.mjs +45 -51
  40. package/src/engines/cron/init.mjs +16 -17
  41. package/src/engines/cron/ipsum.mjs +65 -60
  42. package/src/engines/cron/syscheck.mjs +30 -30
  43. package/src/engines/emailer/index.mjs +1 -2
  44. package/src/engines/emailer/queue.mjs +14 -20
  45. package/src/engines/emailer/transporter.mjs +86 -74
  46. package/src/engines/geoip/index.mjs +23 -28
  47. package/src/engines/http/index.mjs +26 -15
  48. package/src/engines/logger/buildErrorEmailBody.mjs +72 -0
  49. package/src/engines/logger/index.mjs +114 -122
  50. package/src/engines/logger/injectStackTrace.mjs +59 -0
  51. package/src/engines/logger/logger_mail.mjs +47 -61
  52. package/src/engines/logger/reopenTransportOnHupSignal.mjs +12 -13
  53. package/src/engines/parser/Parser.mjs +77 -60
  54. package/src/engines/parser/index.mjs +1 -1
  55. package/src/engines/schema/diffObjs.mjs +41 -0
  56. package/src/engines/schema/index.mjs +4 -0
  57. package/src/engines/schema/input.mjs +54 -0
  58. package/src/engines/schema/output.mjs +66 -0
  59. package/src/engines/socket/index.mjs +44 -46
  60. package/src/index.mjs +15 -10
  61. package/src/middleware/auth/basic.mjs +41 -40
  62. package/src/middleware/auth/custom.mjs +10 -13
  63. package/src/middleware/auth/guest.mjs +27 -27
  64. package/src/middleware/auth/passport/index.mjs +374 -0
  65. package/src/middleware/auth/passport/session/index.mjs +43 -0
  66. package/src/middleware/auth/{credentials → passport}/session/store.mjs +35 -15
  67. package/src/middleware/auth/passport/session/store_koa_redis.mjs +3 -0
  68. package/src/middleware/context/cache/index.mjs +78 -33
  69. package/src/middleware/context/cache/options.mjs +19 -21
  70. package/src/middleware/context/db.mjs +45 -20
  71. package/src/middleware/context/index.mjs +12 -12
  72. package/src/middleware/extra.mjs +4 -5
  73. package/src/middleware/http/body.mjs +25 -25
  74. package/src/middleware/http/catcher.mjs +81 -8
  75. package/src/middleware/http/custom_blacklist.mjs +19 -16
  76. package/src/middleware/http/headers.mjs +37 -34
  77. package/src/middleware/http/ratelimit.mjs +16 -23
  78. package/src/middleware/http/request.mjs +60 -65
  79. package/src/middleware/routes/catch_js_error.mjs +30 -23
  80. package/src/middleware/routes/robots.mjs +4 -7
  81. package/src/middleware/routes/router/crud/attachCrudRoutes.mjs +108 -90
  82. package/src/middleware/routes/router/crud/getCrudConfig.mjs +31 -55
  83. package/src/middleware/routes/router/defaults.mjs +6 -19
  84. package/src/middleware/routes/router/index.mjs +17 -21
  85. package/src/middleware/routes/router/queries/attachQueriesRoutes.mjs +227 -50
  86. package/src/middleware/routes/router/queries/getQueriesConfig.mjs +45 -55
  87. package/src/middleware/routes/router/utils.mjs +41 -26
  88. package/src/middleware/ssr/context.mjs +5 -7
  89. package/src/middleware/ssr/html.mjs +66 -43
  90. package/src/middleware/ssr/loader.mjs +11 -14
  91. package/src/middleware/ssr/ssr_render.mjs +39 -22
  92. package/src/middleware/static/index.mjs +33 -14
  93. package/src/middleware/vite/devserver.mjs +38 -22
  94. package/src/middleware/vite/watcher.mjs +12 -14
  95. package/src/server-cron.mjs +13 -8
  96. package/src/server-dev.mjs +13 -16
  97. package/src/server.mjs +49 -51
  98. package/template/.agent/skills/miolo-app-arch/SKILL.md +218 -0
  99. package/template/.agent/skills/miolo-auth/SKILL.md +450 -0
  100. package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
  101. package/template/.agent/skills/miolo-database/SKILL.md +358 -0
  102. package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
  103. package/template/.agent/skills/miolo-routing/SKILL.md +326 -0
  104. package/template/.agent/skills/miolo-schemas/SKILL.md +329 -0
  105. package/template/.agent/skills/miolo-session-context/SKILL.md +397 -0
  106. package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
  107. package/template/.editorconfig +18 -0
  108. package/template/.env +120 -0
  109. package/template/biome.json +63 -0
  110. package/template/components.json +21 -0
  111. package/template/db/init.sh +89 -0
  112. package/template/db/sql/00_drop.sql +2 -0
  113. package/template/db/sql/01_users.sql +31 -0
  114. package/template/db/sql/02_todos.sql +20 -0
  115. package/template/docker/Dockerfile +13 -0
  116. package/template/docker/docker-compose.yaml +79 -0
  117. package/template/gitignore +42 -0
  118. package/template/jsconfig.json +18 -0
  119. package/template/package.json +88 -0
  120. package/template/postcss.config.js +9 -0
  121. package/template/src/cli/App.jsx +25 -0
  122. package/template/src/cli/components/JsonTreeViewer.jsx +128 -0
  123. package/template/src/cli/components/shadcn-io/spinner/index.jsx +232 -0
  124. package/template/src/cli/components/stepper.jsx +408 -0
  125. package/template/src/cli/components/ui/avatar.jsx +36 -0
  126. package/template/src/cli/components/ui/badge.jsx +31 -0
  127. package/template/src/cli/components/ui/breadcrumb.jsx +97 -0
  128. package/template/src/cli/components/ui/card.jsx +73 -0
  129. package/template/src/cli/components/ui/collapsible.jsx +16 -0
  130. package/template/src/cli/components/ui/dropdown-menu.jsx +179 -0
  131. package/template/src/cli/components/ui/field.jsx +217 -0
  132. package/template/src/cli/components/ui/input.jsx +19 -0
  133. package/template/src/cli/components/ui/label.jsx +17 -0
  134. package/template/src/cli/components/ui/pagination.jsx +99 -0
  135. package/template/src/cli/components/ui/patched/alert.jsx +56 -0
  136. package/template/src/cli/components/ui/patched/button.jsx +45 -0
  137. package/template/src/cli/components/ui/patched/dialog.jsx +114 -0
  138. package/template/src/cli/components/ui/patched/sidebar.jsx +660 -0
  139. package/template/src/cli/components/ui/select.jsx +141 -0
  140. package/template/src/cli/components/ui/separator.jsx +21 -0
  141. package/template/src/cli/components/ui/sheet.jsx +115 -0
  142. package/template/src/cli/components/ui/skeleton.jsx +13 -0
  143. package/template/src/cli/components/ui/sonner.jsx +22 -0
  144. package/template/src/cli/components/ui/switch.jsx +25 -0
  145. package/template/src/cli/components/ui/table.jsx +88 -0
  146. package/template/src/cli/components/ui/textarea.jsx +16 -0
  147. package/template/src/cli/components/ui/tooltip.jsx +45 -0
  148. package/template/src/cli/config/store_keys.mjs +2 -0
  149. package/template/src/cli/context/data/DataContext.jsx +5 -0
  150. package/template/src/cli/context/data/DataProvider.jsx +44 -0
  151. package/template/src/cli/context/data/useBreads.mjs +15 -0
  152. package/template/src/cli/context/data/useDataContext.mjs +4 -0
  153. package/template/src/cli/context/session/SessionContext.mjs +4 -0
  154. package/template/src/cli/context/session/SessionProvider.jsx +31 -0
  155. package/template/src/cli/context/session/makePermissioner.mjs +34 -0
  156. package/template/src/cli/context/session/useSessionContext.mjs +6 -0
  157. package/template/src/cli/context/theme/ThemeContext.mjs +4 -0
  158. package/template/src/cli/context/theme/ThemeProvider.jsx +49 -0
  159. package/template/src/cli/context/theme/useThemeContext.mjs +6 -0
  160. package/template/src/cli/context/ui/UIContext.jsx +5 -0
  161. package/template/src/cli/context/ui/UIProvider.jsx +16 -0
  162. package/template/src/cli/context/ui/useUIContext.mjs +4 -0
  163. package/template/src/cli/context/util.mjs +17 -0
  164. package/template/src/cli/entry-cli.jsx +33 -0
  165. package/template/src/cli/hooks/useIsMobile.mjs +19 -0
  166. package/template/src/cli/hooks/useStoragedState.mjs +63 -0
  167. package/template/src/cli/index.html +29 -0
  168. package/template/src/cli/layout/app-sidebar.jsx +25 -0
  169. package/template/src/cli/layout/main-layout.jsx +63 -0
  170. package/template/src/cli/layout/nav-last-todos.jsx +72 -0
  171. package/template/src/cli/layout/nav-main.jsx +39 -0
  172. package/template/src/cli/layout/nav-user.jsx +105 -0
  173. package/template/src/cli/layout/prop-switcher.jsx +93 -0
  174. package/template/src/cli/lib/utils.mjs +10 -0
  175. package/template/src/cli/pages/Index.jsx +13 -0
  176. package/template/src/cli/pages/IndexOffline.jsx +13 -0
  177. package/template/src/cli/pages/IndexOnline.jsx +18 -0
  178. package/template/src/cli/pages/dash/Dashboard.jsx +29 -0
  179. package/template/src/cli/pages/offline/Login.jsx +43 -0
  180. package/template/src/cli/pages/offline/LoginForm.jsx +115 -0
  181. package/template/src/cli/pages/security/Security.jsx +39 -0
  182. package/template/src/cli/pages/security/SecurityForm.jsx +106 -0
  183. package/template/src/cli/pages/todos/TodoActions.jsx +99 -0
  184. package/template/src/cli/pages/todos/TodoAdd.jsx +43 -0
  185. package/template/src/cli/pages/todos/TodoList.jsx +60 -0
  186. package/template/src/cli/pages/todos/Todos.jsx +23 -0
  187. package/template/src/cli/pages/todos/context/TodosContext.jsx +5 -0
  188. package/template/src/cli/pages/todos/context/TodosProvider.jsx +191 -0
  189. package/template/src/cli/pages/todos/context/useTodosContext.mjs +4 -0
  190. package/template/src/ns/models/Todo.mjs +29 -0
  191. package/template/src/ns/models/TodoList.mjs +8 -0
  192. package/template/src/ns/models/User.mjs +40 -0
  193. package/template/src/server/bot/check_today.mjs +10 -0
  194. package/template/src/server/io/cache/base.mjs +21 -0
  195. package/template/src/server/io/db/filter.mjs +92 -0
  196. package/template/src/server/io/db/todos/delete.mjs +29 -0
  197. package/template/src/server/io/db/todos/find.mjs +13 -0
  198. package/template/src/server/io/db/todos/read.mjs +83 -0
  199. package/template/src/server/io/db/todos/toggle.mjs +37 -0
  200. package/template/src/server/io/db/todos/upsave.mjs +32 -0
  201. package/template/src/server/io/db/triggers/user.mjs +13 -0
  202. package/template/src/server/io/db/users/auth.mjs +132 -0
  203. package/template/src/server/io/db/users/pwd.mjs +38 -0
  204. package/template/src/server/io/db/users/save.mjs +17 -0
  205. package/template/src/server/miolo/auth/basic.mjs +15 -0
  206. package/template/src/server/miolo/auth/guest.mjs +3 -0
  207. package/template/src/server/miolo/auth/passport.mjs +73 -0
  208. package/template/src/server/miolo/cache.mjs +11 -0
  209. package/template/src/server/miolo/cron/foo.mjs +7 -0
  210. package/template/src/server/miolo/cron/index.mjs +28 -0
  211. package/template/src/server/miolo/cron/invalidate.mjs +21 -0
  212. package/template/src/server/miolo/db.mjs +36 -0
  213. package/template/src/server/miolo/http.mjs +14 -0
  214. package/template/src/server/miolo/index.mjs +43 -0
  215. package/template/src/server/miolo/routes/crud.mjs +16 -0
  216. package/template/src/server/miolo/routes/index.mjs +8 -0
  217. package/template/src/server/miolo/ssr/entry-server.jsx +13 -0
  218. package/template/src/server/miolo/ssr/loader.mjs +18 -0
  219. package/template/src/server/routes/index.mjs +66 -0
  220. package/template/src/server/routes/todos/mod.mjs +52 -0
  221. package/template/src/server/routes/todos/read.mjs +45 -0
  222. package/template/src/server/routes/todos/special.mjs +47 -0
  223. package/template/src/server/routes/users/user.mjs +54 -0
  224. package/template/src/server/server.mjs +10 -0
  225. package/template/src/server/utils/crypt.mjs +38 -0
  226. package/template/src/server/utils/io.mjs +15 -0
  227. package/template/src/server/utils/pwdfor.mjs +25 -0
  228. package/template/src/server/utils/schema.mjs +22 -0
  229. package/template/src/static/img/default/profile.png +0 -0
  230. package/template/src/static/img/favicon.ico +0 -0
  231. package/template/src/static/img/miolo_logo.png +0 -0
  232. package/template/src/static/img/miolo_name.png +0 -0
  233. package/template/src/static/public/manifest.json +21 -0
  234. package/template/src/static/public/sw.js +79 -0
  235. package/template/src/static/style/globals.css +156 -0
  236. package/template/src/static/style/json-tree.css +54 -0
  237. package/template/src/static/style/skeleton.css +49 -0
  238. package/bin/prod-build/build-client.mjs +0 -67
  239. package/bin/prod-build/build-server.mjs +0 -58
  240. package/bin/prod-run/restart.mjs +0 -9
  241. package/bin/prod-run/start.mjs +0 -15
  242. package/bin/prod-run/stop.mjs +0 -20
  243. package/src/engines/logger/verify.mjs +0 -22
  244. package/src/middleware/auth/credentials/index.mjs +0 -151
  245. package/src/middleware/auth/credentials/session/index.mjs +0 -24
  246. package/src/middleware/auth/credentials/session/store_koa_redis.mjs +0 -3
@@ -0,0 +1,49 @@
1
+ import { useEffect, useState } from "react"
2
+ import { THEME_STORAGE_KEY } from "#cli/config/store_keys.mjs"
3
+ import ThemeContext from "./ThemeContext.mjs"
4
+
5
+ export default function ThemeProvider({ children, defaultTheme = "system" }) {
6
+ const [theme, setTheme] = useState(() =>
7
+ typeof localStorage !== "undefined"
8
+ ? localStorage.getItem(THEME_STORAGE_KEY) || defaultTheme
9
+ : defaultTheme
10
+ )
11
+ const [isDark, setIsDark] = useState(false)
12
+
13
+ useEffect(() => {
14
+ const root = window.document.documentElement
15
+
16
+ root.classList.remove("light", "dark")
17
+
18
+ if (theme === "system") {
19
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
20
+ ? "dark"
21
+ : "light"
22
+
23
+ root.classList.add(systemTheme)
24
+ setIsDark(systemTheme === "dark")
25
+ return
26
+ }
27
+
28
+ root.classList.add(theme)
29
+ setIsDark(theme === "dark")
30
+ }, [theme])
31
+
32
+ return (
33
+ <ThemeContext.Provider
34
+ value={{
35
+ theme,
36
+ setTheme: (theme) => {
37
+ if (typeof localStorage !== "undefined") {
38
+ localStorage.setItem(THEME_STORAGE_KEY, theme)
39
+ }
40
+ setTheme(theme)
41
+ },
42
+ isDark,
43
+ isLight: () => !isDark
44
+ }}
45
+ >
46
+ {children}
47
+ </ThemeContext.Provider>
48
+ )
49
+ }
@@ -0,0 +1,6 @@
1
+ import { useContext } from "react"
2
+ import ThemeContext from "./ThemeContext.mjs"
3
+
4
+ const useThemeContext = () => useContext(ThemeContext)
5
+
6
+ export default useThemeContext
@@ -0,0 +1,5 @@
1
+ import { createContext } from "react"
2
+
3
+ const UIContext = createContext()
4
+
5
+ export default UIContext
@@ -0,0 +1,16 @@
1
+ import { toast } from "sonner"
2
+ import UIContext from "./UIContext.jsx"
3
+
4
+ const UIProvider = ({ children }) => {
5
+ return (
6
+ <UIContext.Provider
7
+ value={{
8
+ toast
9
+ }}
10
+ >
11
+ {children}
12
+ </UIContext.Provider>
13
+ )
14
+ }
15
+
16
+ export default UIProvider
@@ -0,0 +1,4 @@
1
+ import { useContext } from "react"
2
+ import UIContext from "./UIContext.jsx"
3
+
4
+ export default () => useContext(UIContext)
@@ -0,0 +1,17 @@
1
+ export function isBrowser() {
2
+ return typeof window !== "undefined"
3
+ }
4
+
5
+ export function isDEVAndVite() {
6
+ if (isBrowser()) {
7
+ return window.__CONTEXT?.development === true
8
+ }
9
+ return process.env.NODE_ENV === "development"
10
+ }
11
+
12
+ export function imgSrc(url) {
13
+ // if (isDEVAndVite()) {
14
+ // return `http://localhost:8005/${url.startsWith("/") ? url.slice(1) : url}`
15
+ // }
16
+ return url
17
+ }
@@ -0,0 +1,33 @@
1
+ import { AppBrowser } from "miolo-react"
2
+ import { hydrateRoot } from "react-dom/client"
3
+ import { BrowserRouter } from "react-router"
4
+ import App from "./App.jsx"
5
+
6
+ import "../static/style/globals.css"
7
+
8
+ const domNode = document.getElementById("root")
9
+
10
+ hydrateRoot(
11
+ domNode,
12
+ <AppBrowser>
13
+ <BrowserRouter>
14
+ <App />
15
+ </BrowserRouter>
16
+ </AppBrowser>
17
+ )
18
+
19
+ // Comprobamos si el navegador del usuario soporta Service Workers
20
+ if ("serviceWorker" in navigator) {
21
+ // Esperamos a que la página cargue completamente para no afectar el rendimiento inicial
22
+ window.addEventListener("load", () => {
23
+ // Apuntamos a la URL pública donde Miolo está sirviendo el archivo
24
+ navigator.serviceWorker
25
+ .register("/sw.js")
26
+ .then((registration) => {
27
+ console.log("Service Worker registrado con éxito. Scope:", registration.scope)
28
+ })
29
+ .catch((error) => {
30
+ console.error("Fallo al registrar el Service Worker:", error)
31
+ })
32
+ })
33
+ }
@@ -0,0 +1,19 @@
1
+ import { useEffect, useState } from "react"
2
+
3
+ const MOBILE_BREAKPOINT = 768
4
+
5
+ export default function useIsMobile() {
6
+ const [isMobile, setIsMobile] = useState(undefined)
7
+
8
+ useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12
+ }
13
+ mql.addEventListener("change", onChange)
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15
+ return () => mql.removeEventListener("change", onChange)
16
+ }, [])
17
+
18
+ return !!isMobile
19
+ }
@@ -0,0 +1,63 @@
1
+ import { useEffect, useState /*useCallback,*/ } from "react"
2
+ import { isBrowser } from "#cli/lib/utils.mjs"
3
+
4
+ const getStorageKey = (key) => {
5
+ if (isBrowser()) {
6
+ return `${encodeURIComponent(window.location.pathname)}_${key || ""}`
7
+ }
8
+ return undefined
9
+ }
10
+
11
+ const getPersisted = (key, defValue) => {
12
+ if (isBrowser()) {
13
+ const k = getStorageKey(key)
14
+ const v = localStorage.getItem(k)
15
+ try {
16
+ const p = JSON.parse(v)
17
+ if (p !== undefined && p !== null) {
18
+ return p
19
+ }
20
+ } catch (_) {}
21
+ }
22
+ return defValue
23
+ }
24
+
25
+ const setPersisted = (key, value) => {
26
+ if (isBrowser()) {
27
+ const k = getStorageKey(key)
28
+
29
+ localStorage.setItem(k, JSON.stringify(value))
30
+ }
31
+ }
32
+
33
+ const useStoragedState = (defValue, key) => {
34
+ const [value, setValue] = useState(getPersisted(key, defValue))
35
+
36
+ /*
37
+ const setStoragedValue = useCallback((newValue) => {
38
+ setPersisted(key, newValue)
39
+ setValue(newValue)
40
+ }, [key])
41
+ return [value, setStoragedValue]
42
+
43
+ */
44
+
45
+ //
46
+ // Returning and exposing setValue, we allow
47
+ // to use functional setValue() too!
48
+ //
49
+
50
+ useEffect(() => {
51
+ setPersisted(key, value)
52
+ }, [key, value])
53
+
54
+ /*
55
+ const wrapSetValue = useCallback((nValue) => {
56
+ setPersisted(key, typeof nValue =='function' ? nValue(value) : nValue)
57
+ setValue(nValue)
58
+ }, [key, value])*/
59
+
60
+ return [value, setValue]
61
+ }
62
+
63
+ export { useStoragedState }
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <title>miolo-sample</title>
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <meta name="description" content="miolo-sample" />
10
+ <meta name="keywords" content="miolo-sample" />
11
+ <meta name="author" content="devel@afialapis.com" />
12
+
13
+ <!-- Touch Icons - iOS and Android 2.1+ 180x180 pixels in size. -->
14
+ <!--<link rel="apple-touch-icon-precomposed" href="/favicon.ico"/>-->
15
+ <!-- Firefox, Chrome, Safari, IE 11+ and Opera. 196x196 pixels in size. -->
16
+ <link rel="icon" href="/favicon.ico" />
17
+
18
+
19
+
20
+ <!-- PWA -->
21
+ <link rel="manifest" href="/manifest.json">
22
+ <meta name="theme-color" content="#fb64b6">
23
+
24
+ </head>
25
+
26
+ <body>
27
+ </body>
28
+
29
+ </html>
@@ -0,0 +1,25 @@
1
+ "use client"
2
+ import {
3
+ Sidebar,
4
+ SidebarContent,
5
+ SidebarFooter,
6
+ SidebarRail
7
+ } from "#cli/components/ui/patched/sidebar.jsx"
8
+ import { NavLastTodos } from "./nav-last-todos.jsx"
9
+ import { NavMain } from "./nav-main.jsx"
10
+ import { NavUser } from "./nav-user.jsx"
11
+
12
+ export function AppSidebar() {
13
+ return (
14
+ <Sidebar collapsible="icon">
15
+ <SidebarContent>
16
+ <NavLastTodos />
17
+ <NavMain />
18
+ </SidebarContent>
19
+ <SidebarFooter>
20
+ <NavUser />
21
+ </SidebarFooter>
22
+ <SidebarRail />
23
+ </Sidebar>
24
+ )
25
+ }
@@ -0,0 +1,63 @@
1
+ import { Fragment } from "react"
2
+ import { Outlet } from "react-router"
3
+ import { Spinner } from "#cli/components/shadcn-io/spinner/index.jsx"
4
+ import {
5
+ Breadcrumb,
6
+ BreadcrumbItem,
7
+ BreadcrumbLink,
8
+ BreadcrumbList,
9
+ BreadcrumbPage,
10
+ BreadcrumbSeparator
11
+ } from "#cli/components/ui/breadcrumb.jsx"
12
+ import {
13
+ SidebarInset,
14
+ SidebarProvider,
15
+ SidebarTrigger
16
+ } from "#cli/components/ui/patched/sidebar.jsx"
17
+ import { Separator } from "#cli/components/ui/separator.jsx"
18
+ import { Toaster } from "#cli/components/ui/sonner.jsx"
19
+ import useDataContext from "#cli/context/data/useDataContext.mjs"
20
+ import { AppSidebar } from "#cli/layout/app-sidebar.jsx"
21
+
22
+ export default function MainLayout() {
23
+ const { breads, loading } = useDataContext()
24
+
25
+ const hasBreads = breads.length > 0
26
+ const firstBreads = breads.length > 1 ? breads.slice(0, -1) : []
27
+ const lastBread = hasBreads ? breads[breads.length - 1] : []
28
+
29
+ return (
30
+ <SidebarProvider>
31
+ <AppSidebar />
32
+ <SidebarInset>
33
+ <header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
34
+ <SidebarTrigger className="-ml-1" />
35
+ <Separator orientation="vertical" className="mr-2 data-[orientation=vertical]:h-4" />
36
+ {hasBreads && (
37
+ <Breadcrumb>
38
+ <BreadcrumbList>
39
+ {firstBreads.map((bread) => {
40
+ return (
41
+ <Fragment key={`bread_${bread[1]}`}>
42
+ <BreadcrumbItem className="hidden md:block">
43
+ <BreadcrumbLink href={bread[0]}>{bread[1]}</BreadcrumbLink>
44
+ </BreadcrumbItem>
45
+ <BreadcrumbSeparator className="hidden md:block" />
46
+ </Fragment>
47
+ )
48
+ })}
49
+ <BreadcrumbItem>
50
+ <BreadcrumbPage>
51
+ {lastBread.length > 1 ? lastBread[1] : lastBread[0]}
52
+ </BreadcrumbPage>
53
+ </BreadcrumbItem>
54
+ </BreadcrumbList>
55
+ </Breadcrumb>
56
+ )}
57
+ </header>
58
+ {loading ? <Spinner /> : <Outlet />}
59
+ <Toaster />
60
+ </SidebarInset>
61
+ </SidebarProvider>
62
+ )
63
+ }
@@ -0,0 +1,72 @@
1
+ "use client"
2
+ import { Cable, ChartSpline, ChevronRight, HousePlug, Route, RouteOff } from "lucide-react"
3
+ import { Link } from "react-router"
4
+
5
+ import {
6
+ Collapsible,
7
+ CollapsibleContent,
8
+ CollapsibleTrigger
9
+ } from "#cli/components/ui/collapsible.jsx"
10
+ import {
11
+ SidebarGroup,
12
+ SidebarGroupLabel,
13
+ SidebarMenu,
14
+ SidebarMenuButton,
15
+ SidebarMenuItem,
16
+ SidebarMenuSub,
17
+ SidebarMenuSubButton,
18
+ SidebarMenuSubItem
19
+ } from "#cli/components/ui/patched/sidebar.jsx"
20
+ import useDataContext from "#cli/context/data/useDataContext.mjs"
21
+
22
+ export function NavLastTodos() {
23
+ const { lastTodos } = useDataContext()
24
+
25
+ return (
26
+ <SidebarGroup>
27
+ <SidebarGroupLabel>Resumen</SidebarGroupLabel>
28
+ <SidebarMenu>
29
+ <SidebarMenuItem>
30
+ <SidebarMenuButton tooltip={"Inicio"}>
31
+ <ChartSpline />
32
+ <Link to="/">Inicio</Link>
33
+ </SidebarMenuButton>
34
+ </SidebarMenuItem>
35
+ {lastTodos.length === 0 ? null : (
36
+ <Collapsible key={"Conectadas"} asChild defaultOpen={true} className="group/collapsible">
37
+ <SidebarMenuItem>
38
+ <CollapsibleTrigger asChild>
39
+ <SidebarMenuButton tooltip={"Últimos todos"}>
40
+ <HousePlug />
41
+ <span>Últimos todos</span>
42
+ <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
43
+ </SidebarMenuButton>
44
+ </CollapsibleTrigger>
45
+ <CollapsibleContent>
46
+ <SidebarMenuSub>
47
+ {lastTodos.map((todo) => (
48
+ <SidebarMenuSubItem key={todo.id}>
49
+ <SidebarMenuSubButton asChild>
50
+ <Link to={`/`}>
51
+ {todo.done ? <RouteOff color="red" /> : <Route color="green" />}
52
+ <span>{todo.description}</span>
53
+ </Link>
54
+ </SidebarMenuSubButton>
55
+ </SidebarMenuSubItem>
56
+ ))}
57
+ </SidebarMenuSub>
58
+ </CollapsibleContent>
59
+ </SidebarMenuItem>
60
+ </Collapsible>
61
+ )}
62
+
63
+ <SidebarMenuItem>
64
+ <SidebarMenuButton tooltip={"Yay!"}>
65
+ <Cable />
66
+ <Link to="/">Yay!</Link>
67
+ </SidebarMenuButton>
68
+ </SidebarMenuItem>
69
+ </SidebarMenu>
70
+ </SidebarGroup>
71
+ )
72
+ }
@@ -0,0 +1,39 @@
1
+ "use client"
2
+ import { ArrowDownUp, Braces } from "lucide-react"
3
+ import { Link } from "react-router"
4
+
5
+ import {
6
+ SidebarGroup,
7
+ SidebarGroupLabel,
8
+ SidebarMenu,
9
+ SidebarMenuButton,
10
+ SidebarMenuItem
11
+ } from "#cli/components/ui/patched/sidebar.jsx"
12
+ // import useSessionContext from '#cli/context/session/useSessionContext.mjs'
13
+
14
+ export function NavMain() {
15
+ // const {permiss} = useSessionContext()
16
+
17
+ return (
18
+ <SidebarGroup>
19
+ <SidebarGroupLabel>Configuración</SidebarGroupLabel>
20
+ <SidebarMenu>
21
+ <SidebarMenuItem>
22
+ <SidebarMenuButton tooltip={"Red"}>
23
+ <ArrowDownUp />
24
+ <Link to="/">Foo</Link>
25
+ </SidebarMenuButton>
26
+ </SidebarMenuItem>
27
+
28
+ {/*permiss.can_user_debug() && (*/}
29
+ <SidebarMenuItem>
30
+ <SidebarMenuButton tooltip={"Dev"}>
31
+ <Braces />
32
+ <Link to="/dev">Bar</Link>
33
+ </SidebarMenuButton>
34
+ </SidebarMenuItem>
35
+ {/*)*/}
36
+ </SidebarMenu>
37
+ </SidebarGroup>
38
+ )
39
+ }
@@ -0,0 +1,105 @@
1
+ "use client"
2
+
3
+ import { ChevronsUpDown, LogOut, Moon, Shield, Sun } from "lucide-react"
4
+ import { useNavigate } from "react-router"
5
+ import { Avatar, AvatarFallback, AvatarImage } from "#cli/components/ui/avatar.jsx"
6
+ import {
7
+ DropdownMenu,
8
+ DropdownMenuContent,
9
+ DropdownMenuItem,
10
+ DropdownMenuLabel,
11
+ DropdownMenuSeparator,
12
+ DropdownMenuTrigger
13
+ } from "#cli/components/ui/dropdown-menu.jsx"
14
+ import {
15
+ SidebarMenu,
16
+ SidebarMenuButton,
17
+ SidebarMenuItem,
18
+ useSidebar
19
+ } from "#cli/components/ui/patched/sidebar.jsx"
20
+ import useSessionContext from "#cli/context/session/useSessionContext.mjs"
21
+ import useThemeContext from "#cli/context/theme/useThemeContext.mjs"
22
+
23
+ export function NavUser() {
24
+ const { isMobile } = useSidebar()
25
+
26
+ const { session, logout, authMethod } = useSessionContext()
27
+ const navigate = useNavigate()
28
+ const { isDark, setTheme } = useThemeContext()
29
+
30
+ const twoLetters = session.name.slice(0, 2).toUpperCase()
31
+ const avatarSrc = `/static/img/default/profile.png`
32
+
33
+ return (
34
+ <SidebarMenu>
35
+ <SidebarMenuItem>
36
+ <DropdownMenu>
37
+ <DropdownMenuTrigger asChild>
38
+ <SidebarMenuButton
39
+ size="lg"
40
+ className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
41
+ >
42
+ <Avatar className="h-8 w-8 rounded-lg">
43
+ <AvatarImage src={avatarSrc} alt={session.name} />
44
+ <AvatarFallback className="rounded-lg">{twoLetters}</AvatarFallback>
45
+ </Avatar>
46
+ <div className="grid flex-1 text-left text-sm leading-tight">
47
+ <span className="truncate font-medium">{session.name}</span>
48
+ <span className="truncate text-xs">{session.email}</span>
49
+ </div>
50
+ <ChevronsUpDown className="ml-auto size-4" />
51
+ </SidebarMenuButton>
52
+ </DropdownMenuTrigger>
53
+ <DropdownMenuContent
54
+ className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
55
+ side={isMobile ? "bottom" : "right"}
56
+ align="end"
57
+ sideOffset={4}
58
+ >
59
+ <DropdownMenuLabel className="p-0 font-normal">
60
+ <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
61
+ <Avatar className="h-8 w-8 rounded-lg">
62
+ <AvatarImage src={avatarSrc} alt={session.name} />
63
+ <AvatarFallback className="rounded-lg">{twoLetters}</AvatarFallback>
64
+ </Avatar>
65
+ <div className="grid flex-1 text-left text-sm leading-tight">
66
+ <span className="truncate font-medium">{session.name}</span>
67
+ <span className="truncate text-xs">{session.email}</span>
68
+ </div>
69
+ </div>
70
+ </DropdownMenuLabel>
71
+ <DropdownMenuSeparator />
72
+ {isDark ? (
73
+ <DropdownMenuItem onSelect={() => setTheme("light")}>
74
+ <Sun />
75
+ Modo día
76
+ </DropdownMenuItem>
77
+ ) : (
78
+ <DropdownMenuItem onSelect={() => setTheme("dark")}>
79
+ <Moon />
80
+ Modo noche
81
+ </DropdownMenuItem>
82
+ )}
83
+ <DropdownMenuSeparator />
84
+ {/*
85
+ <DropdownMenuItem onSelect={() => navigate('/profile')}>
86
+ <User/>
87
+ Perfil
88
+ </DropdownMenuItem>
89
+ */}
90
+ <DropdownMenuItem onSelect={() => navigate("/security")}>
91
+ <Shield />
92
+ Seguridad
93
+ </DropdownMenuItem>
94
+ {["local", "google", "basic"].includes(authMethod) && (
95
+ <DropdownMenuItem onSelect={() => logout()}>
96
+ <LogOut />
97
+ Cerrar sesión
98
+ </DropdownMenuItem>
99
+ )}
100
+ </DropdownMenuContent>
101
+ </DropdownMenu>
102
+ </SidebarMenuItem>
103
+ </SidebarMenu>
104
+ )
105
+ }
@@ -0,0 +1,93 @@
1
+ "use client"
2
+
3
+ import { Cable, ChevronsUpDown, House } from "lucide-react"
4
+ import { useState } from "react"
5
+ import { useLocation } from "react-router"
6
+
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuItem,
11
+ DropdownMenuLabel,
12
+ DropdownMenuSeparator,
13
+ //DropdownMenuShortcut,
14
+ DropdownMenuTrigger
15
+ } from "#cli/components/ui/dropdown-menu.jsx"
16
+ import {
17
+ SidebarMenu,
18
+ SidebarMenuButton,
19
+ SidebarMenuItem,
20
+ useSidebar
21
+ } from "#cli/components/ui/patched/sidebar.jsx"
22
+ import useDataContext from "#cli/context/data/useDataContext.mjs"
23
+
24
+ export function PropSwitcher() {
25
+ const { isMobile } = useSidebar()
26
+ const location = useLocation()
27
+
28
+ const { properties } = useDataContext()
29
+ const [activeProp, setActiveProp] = useState(properties[0])
30
+
31
+ // TODO CHeck w
32
+ if (location.pathname === "/connect") {
33
+ return null
34
+ }
35
+
36
+ if (!activeProp) {
37
+ return null
38
+ }
39
+
40
+ return (
41
+ <SidebarMenu>
42
+ <SidebarMenuItem>
43
+ <DropdownMenu>
44
+ <DropdownMenuTrigger asChild>
45
+ <SidebarMenuButton
46
+ size="lg"
47
+ className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
48
+ >
49
+ <div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
50
+ <House className="size-4" />
51
+ </div>
52
+ <div className="grid flex-1 text-left text-sm leading-tight">
53
+ <span className="truncate font-medium">{activeProp.name}</span>
54
+ {/*<span className="truncate text-xs">{activeProp.plan}</span>*/}
55
+ </div>
56
+ <ChevronsUpDown className="ml-auto" />
57
+ </SidebarMenuButton>
58
+ </DropdownMenuTrigger>
59
+ <DropdownMenuContent
60
+ className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
61
+ align="start"
62
+ side={isMobile ? "bottom" : "right"}
63
+ sideOffset={4}
64
+ >
65
+ <DropdownMenuLabel className="text-muted-foreground text-xs">
66
+ Propiedades
67
+ </DropdownMenuLabel>
68
+ {properties.map((prop, _index) => (
69
+ <DropdownMenuItem
70
+ key={prop.name}
71
+ onClick={() => setActiveProp(prop)}
72
+ className="gap-2 p-2"
73
+ >
74
+ <div className="flex size-6 items-center justify-center rounded-md border">
75
+ <House className="size-3.5 shrink-0" />
76
+ </div>
77
+ {prop.name}
78
+ {/*<DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut>*/}
79
+ </DropdownMenuItem>
80
+ ))}
81
+ <DropdownMenuSeparator />
82
+ <DropdownMenuItem className="gap-2 p-2">
83
+ <div className="flex size-6 items-center justify-center rounded-md border bg-transparent">
84
+ <Cable className="size-4" />
85
+ </div>
86
+ <div className="text-muted-foreground font-medium">Conectar propiedad</div>
87
+ </DropdownMenuItem>
88
+ </DropdownMenuContent>
89
+ </DropdownMenu>
90
+ </SidebarMenuItem>
91
+ </SidebarMenu>
92
+ )
93
+ }
@@ -0,0 +1,10 @@
1
+ import { clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs) {
5
+ return twMerge(clsx(inputs))
6
+ }
7
+
8
+ export function isBrowser() {
9
+ return typeof window !== "undefined"
10
+ }
@@ -0,0 +1,13 @@
1
+ import useSessionContext from "#cli/context/session/useSessionContext.mjs"
2
+ import IndexOffline from "#cli/pages/IndexOffline.jsx"
3
+ import IndexOnline from "#cli/pages/IndexOnline.jsx"
4
+
5
+ export default function Index() {
6
+ const { authenticated } = useSessionContext()
7
+
8
+ if (!authenticated) {
9
+ return <IndexOffline />
10
+ }
11
+
12
+ return <IndexOnline />
13
+ }