🎉 Init project from scratch
This commit is contained in:
		
							
								
								
									
										5
									
								
								pkg/view/src/.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pkg/view/src/.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| { | ||||
|   "printWidth": 120, | ||||
|   "tabWidth": 2, | ||||
|   "singleQuote": false | ||||
| } | ||||
							
								
								
									
										197
									
								
								pkg/view/src/assets/fonts/fonts.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								pkg/view/src/assets/fonts/fonts.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| :root { | ||||
|   --bs-body-font-family: "IBM Plex Serif", "Noto Serif SC", sans-serif !important; | ||||
| } | ||||
|  | ||||
| html, | ||||
| body { | ||||
|   font-family: var(--bs-body-font-family); | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-100 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 100; | ||||
|   src: url("./ibm-plex-serif-v19-latin-100.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-100italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 100; | ||||
|   src: url("./ibm-plex-serif-v19-latin-100italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-200 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 200; | ||||
|   src: url("./ibm-plex-serif-v19-latin-200.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-200italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 200; | ||||
|   src: url("./ibm-plex-serif-v19-latin-200italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-300 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 300; | ||||
|   src: url("./ibm-plex-serif-v19-latin-300.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-300italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 300; | ||||
|   src: url("./ibm-plex-serif-v19-latin-300italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-regular - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 400; | ||||
|   src: url("./ibm-plex-serif-v19-latin-regular.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 400; | ||||
|   src: url("./ibm-plex-serif-v19-latin-italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-500 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 500; | ||||
|   src: url("./ibm-plex-serif-v19-latin-500.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-500italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 500; | ||||
|   src: url("./ibm-plex-serif-v19-latin-500italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-600 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 600; | ||||
|   src: url("./ibm-plex-serif-v19-latin-600.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-600italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 600; | ||||
|   src: url("./ibm-plex-serif-v19-latin-600italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-700 - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: normal; | ||||
|   font-weight: 700; | ||||
|   src: url("./ibm-plex-serif-v19-latin-700.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* ibm-plex-serif-700italic - latin */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "IBM Plex Serif"; | ||||
|   font-style: italic; | ||||
|   font-weight: 700; | ||||
|   src: url("./ibm-plex-serif-v19-latin-700italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-200 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 200; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-200.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-300 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 300; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-300.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-regular - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 400; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-regular.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-500 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 500; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-500.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-600 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 600; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-600.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-700 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 700; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-700.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
|  | ||||
| /* noto-serif-sc-900 - chinese-simplified */ | ||||
| @font-face { | ||||
|   font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ | ||||
|   font-family: "Noto Serif SC"; | ||||
|   font-style: normal; | ||||
|   font-weight: 900; | ||||
|   src: url("./noto-serif-sc-v22-chinese-simplified-900.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-100italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-200italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-300italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-500italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-600italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-700italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-italic.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-italic.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/ibm-plex-serif-v19-latin-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pkg/view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										8
									
								
								pkg/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| @tailwind base; | ||||
| @tailwind components; | ||||
| @tailwind utilities; | ||||
|  | ||||
| html, body { | ||||
|     padding: 0; | ||||
|     margin: 0; | ||||
| } | ||||
							
								
								
									
										32
									
								
								pkg/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								pkg/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import "solid-devtools"; | ||||
|  | ||||
| /* @refresh reload */ | ||||
| import { render } from "solid-js/web"; | ||||
|  | ||||
| import "./index.css"; | ||||
| import "./assets/fonts/fonts.css"; | ||||
| import { lazy } from "solid-js"; | ||||
| import { Route, Router } from "@solidjs/router"; | ||||
|  | ||||
| import RootLayout from "./layouts/RootLayout.tsx"; | ||||
| import { UserinfoProvider } from "./stores/userinfo.tsx"; | ||||
| import { WellKnownProvider } from "./stores/wellKnown.tsx"; | ||||
|  | ||||
| const root = document.getElementById("root"); | ||||
|  | ||||
| render(() => ( | ||||
|   <WellKnownProvider> | ||||
|     <UserinfoProvider> | ||||
|       <Router root={RootLayout}> | ||||
|         <Route path="/" component={lazy(() => import("./pages/dashboard.tsx"))} /> | ||||
|         <Route path="/security" component={lazy(() => import("./pages/security.tsx"))} /> | ||||
|         <Route path="/personalise" component={lazy(() => import("./pages/personalise.tsx"))} /> | ||||
|         <Route path="/auth/login" component={lazy(() => import("./pages/auth/login.tsx"))} /> | ||||
|         <Route path="/auth/register" component={lazy(() => import("./pages/auth/register.tsx"))} /> | ||||
|         <Route path="/auth/oauth/connect" component={lazy(() => import("./pages/auth/connect.tsx"))} /> | ||||
|         <Route path="/auth/oauth/callback" component={lazy(() => import("./pages/auth/callback.tsx"))} /> | ||||
|         <Route path="/users/me/confirm" component={lazy(() => import("./pages/users/confirm.tsx"))} /> | ||||
|       </Router> | ||||
|     </UserinfoProvider> | ||||
|   </WellKnownProvider> | ||||
| ), root!); | ||||
							
								
								
									
										46
									
								
								pkg/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| import Navbar from "./shared/Navbar.tsx"; | ||||
| import { readProfiles, useUserinfo } from "../stores/userinfo.tsx"; | ||||
| import { createEffect, createSignal, Show } from "solid-js"; | ||||
| import { readWellKnown } from "../stores/wellKnown.tsx"; | ||||
| import { BeforeLeaveEventArgs, useBeforeLeave, useLocation, useNavigate } from "@solidjs/router"; | ||||
|  | ||||
| export default function RootLayout(props: any) { | ||||
|   const [ready, setReady] = createSignal(false); | ||||
|  | ||||
|   Promise.all([readWellKnown(), readProfiles()]).then(() => setReady(true)); | ||||
|  | ||||
|   const navigate = useNavigate(); | ||||
|   const userinfo = useUserinfo(); | ||||
|  | ||||
|   const location = useLocation(); | ||||
|  | ||||
|   createEffect(() => { | ||||
|     if (ready()) { | ||||
|       keepGate(location.pathname); | ||||
|     } | ||||
|   }, [ready, userinfo]); | ||||
|  | ||||
|   function keepGate(path: string, e?: BeforeLeaveEventArgs) { | ||||
|     const whitelist = ["/auth/login", "/auth/register", "/users/me/confirm"]; | ||||
|  | ||||
|     if (!userinfo?.isLoggedIn && !whitelist.includes(path)) { | ||||
|       if (!e?.defaultPrevented) e?.preventDefault(); | ||||
|       navigate(`/auth/login?redirect_uri=${path}`); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   useBeforeLeave((e: BeforeLeaveEventArgs) => keepGate(e.to.toString().split("?")[0], e)); | ||||
|  | ||||
|   return ( | ||||
|     <Show when={ready()} fallback={ | ||||
|       <div class="h-screen w-screen flex justify-center items-center"> | ||||
|         <div> | ||||
|           <span class="loading loading-lg loading-infinity"></span> | ||||
|         </div> | ||||
|       </div> | ||||
|     }> | ||||
|       <Navbar /> | ||||
|       <main class="h-[calc(100vh-68px)] mt-[68px]">{props.children}</main> | ||||
|     </Show> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										118
									
								
								pkg/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								pkg/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| import { For, Match, Show, Switch } from "solid-js"; | ||||
| import { clearUserinfo, useUserinfo } from "../../stores/userinfo.tsx"; | ||||
| import { useNavigate } from "@solidjs/router"; | ||||
| import { useWellKnown } from "../../stores/wellKnown.tsx"; | ||||
|  | ||||
| interface MenuItem { | ||||
|   label: string; | ||||
|   href?: string; | ||||
|   children?: MenuItem[]; | ||||
| } | ||||
|  | ||||
| export default function Navbar() { | ||||
|   const nav: MenuItem[] = [ | ||||
|     { | ||||
|       label: "You", children: [ | ||||
|         { label: "Dashboard", href: "/" }, | ||||
|         { label: "Security", href: "/security" }, | ||||
|         { label: "Personalise", href: "/personalise" } | ||||
|       ] | ||||
|     } | ||||
|   ]; | ||||
|  | ||||
|   const wellKnown = useWellKnown(); | ||||
|   const userinfo = useUserinfo(); | ||||
|   const navigate = useNavigate(); | ||||
|  | ||||
|   function logout() { | ||||
|     clearUserinfo(); | ||||
|     navigate("/auth/login"); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div class="navbar bg-base-100 shadow-md px-5 z-10 fixed top-0"> | ||||
|       <div class="navbar-start"> | ||||
|         <div class="dropdown"> | ||||
|           <div tabIndex={0} role="button" class="btn btn-ghost lg:hidden"> | ||||
|             <svg | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               class="h-5 w-5" | ||||
|               fill="none" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke="currentColor" | ||||
|             > | ||||
|               <path | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 stroke-width="2" | ||||
|                 d="M4 6h16M4 12h8m-8 6h16" | ||||
|               /> | ||||
|             </svg> | ||||
|           </div> | ||||
|           <ul | ||||
|             tabIndex={0} | ||||
|             class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52" | ||||
|           > | ||||
|             <For each={nav}> | ||||
|               {(item) => ( | ||||
|                 <li> | ||||
|                   <a href={item.href}>{item.label}</a> | ||||
|                   <Show when={item.children}> | ||||
|                     <ul class="p-2"> | ||||
|                       <For each={item.children}> | ||||
|                         {(item) => | ||||
|                           <li> | ||||
|                             <a href={item.href}>{item.label}</a> | ||||
|                           </li> | ||||
|                         } | ||||
|                       </For> | ||||
|                     </ul> | ||||
|                   </Show> | ||||
|                 </li> | ||||
|               )} | ||||
|             </For> | ||||
|           </ul> | ||||
|         </div> | ||||
|         <a href="/" class="btn btn-ghost text-xl"> | ||||
|           {wellKnown?.name ?? "Goatpass"} | ||||
|         </a> | ||||
|       </div> | ||||
|       <div class="navbar-center hidden lg:flex"> | ||||
|         <ul class="menu menu-horizontal px-1"> | ||||
|           <For each={nav}> | ||||
|             {(item) => ( | ||||
|               <li> | ||||
|                 <Show when={item.children} fallback={<a href={item.href}>{item.label}</a>}> | ||||
|                   <details> | ||||
|                     <summary> | ||||
|                       <a href={item.href}>{item.label}</a> | ||||
|                     </summary> | ||||
|                     <ul class="p-2"> | ||||
|                       <For each={item.children}> | ||||
|                         {(item) => | ||||
|                           <li> | ||||
|                             <a href={item.href}>{item.label}</a> | ||||
|                           </li> | ||||
|                         } | ||||
|                       </For> | ||||
|                     </ul> | ||||
|                   </details> | ||||
|                 </Show> | ||||
|               </li> | ||||
|             )} | ||||
|           </For> | ||||
|         </ul> | ||||
|       </div> | ||||
|       <div class="navbar-end pe-5"> | ||||
|         <Switch> | ||||
|           <Match when={userinfo?.isLoggedIn}> | ||||
|             <button type="button" class="btn btn-sm btn-ghost" onClick={() => logout()}>Logout</button> | ||||
|           </Match> | ||||
|           <Match when={!userinfo?.isLoggedIn}> | ||||
|             <a href="/auth/login" class="btn btn-sm btn-primary">Login</a> | ||||
|           </Match> | ||||
|         </Switch> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										108
									
								
								pkg/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								pkg/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| import { getAtk, readProfiles, useUserinfo } from "../stores/userinfo.tsx"; | ||||
| import { createSignal, For, Show } from "solid-js"; | ||||
|  | ||||
| export default function DashboardPage() { | ||||
|   const userinfo = useUserinfo(); | ||||
|  | ||||
|   const [error, setError] = createSignal<string | null>(null); | ||||
|  | ||||
|   function getGreeting() { | ||||
|     const currentHour = new Date().getHours(); | ||||
|  | ||||
|     if (currentHour >= 0 && currentHour < 12) { | ||||
|       return "Good morning! Wishing you a day filled with joy and success. ☀️"; | ||||
|     } else if (currentHour >= 12 && currentHour < 18) { | ||||
|       return "Afternoon! Hope you have a productive and joyful afternoon! ☀️"; | ||||
|     } else { | ||||
|       return "Good evening! Wishing you a relaxing and pleasant evening. 🌙"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async function readNotification(item: any) { | ||||
|     const res = await fetch(`/api/notifications/${item.id}/read`, { | ||||
|       method: "PUT", | ||||
|       headers: { Authorization: `Bearer ${getAtk()}` } | ||||
|     }); | ||||
|     if (res.status !== 200) { | ||||
|       setError(await res.text()); | ||||
|     } else { | ||||
|       await readProfiles(); | ||||
|       setError(null); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div class="max-w-[720px] mx-auto px-5 pt-12"> | ||||
|       <div id="greeting" class="px-5"> | ||||
|         <h1 class="text-2xl font-bold">{userinfo?.displayName}</h1> | ||||
|         <p>{getGreeting()}</p> | ||||
|       </div> | ||||
|  | ||||
|       <div id="alerts"> | ||||
|         <Show when={!userinfo?.meta?.confirmed_at}> | ||||
|           <div role="alert" class="alert alert-warning mt-5"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||
|                  viewBox="0 0 24 24"> | ||||
|               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||
|                     d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /> | ||||
|             </svg> | ||||
|             <div> | ||||
|               <span>Your account isn't confirmed yet. Please check your inbox and confirm your account.</span> <br /> | ||||
|               <span>Otherwise your account will be deactivate after 48 hours.</span> | ||||
|             </div> | ||||
|           </div> | ||||
|         </Show> | ||||
|         <Show when={error()}> | ||||
|           <div role="alert" class="alert alert-error mt-5"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||
|                  viewBox="0 0 24 24"> | ||||
|               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||
|                     d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||||
|             </svg> | ||||
|             <span class="capitalize">{error()}</span> | ||||
|           </div> | ||||
|         </Show> | ||||
|       </div> | ||||
|  | ||||
|       <div class="card shadow-xl mt-5"> | ||||
|         <div class="card-body"> | ||||
|           <h2 class="card-title">Notifications</h2> | ||||
|           <div class="bg-base-200 mt-3 mx-[-32px]"> | ||||
|             <Show when={userinfo?.meta?.notifications?.length <= 0}> | ||||
|               <table class="table"> | ||||
|                 <tbody> | ||||
|                 <tr> | ||||
|                   <td class="px-[32px]">You're done! There are no notifications unread for you.</td> | ||||
|                 </tr> | ||||
|                 </tbody> | ||||
|               </table> | ||||
|             </Show> | ||||
|             <Show when={userinfo?.meta?.notifications?.length > 0}> | ||||
|               <table class="table"> | ||||
|                 <tbody> | ||||
|                 <For each={userinfo?.meta?.notifications}> | ||||
|                   {item => | ||||
|                     <tr> | ||||
|                       <td class="px-[32px]"> | ||||
|                         <h2 class="font-bold">{item.subject}</h2> | ||||
|                         <p>{item.content}</p> | ||||
|                         <div class="flex gap-2"> | ||||
|                           <Show when={item.is_important}> | ||||
|                             <span class="font-bold">Important</span> | ||||
|                           </Show> | ||||
|                           <a class="link" onClick={() => readNotification(item)}>Mark as read</a> | ||||
|                         </div> | ||||
|                       </td> | ||||
|                     </tr> | ||||
|                   } | ||||
|                 </For> | ||||
|                 </tbody> | ||||
|               </table> | ||||
|             </Show> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										95
									
								
								pkg/view/src/stores/userinfo.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								pkg/view/src/stores/userinfo.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| import Cookie from "universal-cookie"; | ||||
| import { createContext, useContext } from "solid-js"; | ||||
| import { createStore } from "solid-js/store"; | ||||
|  | ||||
| export interface Userinfo { | ||||
|   isLoggedIn: boolean, | ||||
|   displayName: string, | ||||
|   profiles: any, | ||||
|   meta: any | ||||
| } | ||||
|  | ||||
| const UserinfoContext = createContext<Userinfo>(); | ||||
|  | ||||
| const defaultUserinfo: Userinfo = { | ||||
|   isLoggedIn: false, | ||||
|   displayName: "Citizen", | ||||
|   profiles: null, | ||||
|   meta: null | ||||
| }; | ||||
|  | ||||
| const [userinfo, setUserinfo] = createStore<Userinfo>(structuredClone(defaultUserinfo)); | ||||
|  | ||||
| export function getAtk(): string { | ||||
|   return new Cookie().get("access_token"); | ||||
| } | ||||
|  | ||||
| export async function refreshAtk() { | ||||
|   const rtk = new Cookie().get("refresh_token"); | ||||
|  | ||||
|   const res = await fetch("/api/auth/token", { | ||||
|     method: "POST", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify({ | ||||
|       refresh_token: rtk, | ||||
|       grant_type: "refresh_token" | ||||
|     }) | ||||
|   }); | ||||
|   if (res.status !== 200) { | ||||
|     console.error(await res.text()) | ||||
|   } else { | ||||
|     const data = await res.json(); | ||||
|     new Cookie().set("access_token", data["access_token"], { path: "/", maxAge: undefined }); | ||||
|     new Cookie().set("refresh_token", data["refresh_token"], { path: "/", maxAge: undefined }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function checkLoggedIn(): boolean { | ||||
|   return new Cookie().get("access_token"); | ||||
| } | ||||
|  | ||||
| export async function readProfiles(recovering = true) { | ||||
|   if (!checkLoggedIn()) return; | ||||
|  | ||||
|   const res = await fetch("/api/users/me", { | ||||
|     headers: { "Authorization": `Bearer ${getAtk()}` } | ||||
|   }); | ||||
|  | ||||
|   if (res.status !== 200) { | ||||
|     if (recovering) { | ||||
|       // Auto retry after refresh access token | ||||
|       await refreshAtk(); | ||||
|       return await readProfiles(false); | ||||
|     } else { | ||||
|       clearUserinfo(); | ||||
|       window.location.reload(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   const data = await res.json(); | ||||
|  | ||||
|   setUserinfo({ | ||||
|     isLoggedIn: true, | ||||
|     displayName: data["nick"], | ||||
|     profiles: null, | ||||
|     meta: data | ||||
|   }); | ||||
| } | ||||
|  | ||||
| export function clearUserinfo() { | ||||
|   new Cookie().remove("access_token", { path: "/", maxAge: undefined }); | ||||
|   new Cookie().remove("refresh_token", { path: "/", maxAge: undefined }); | ||||
|   setUserinfo(defaultUserinfo); | ||||
| } | ||||
|  | ||||
| export function UserinfoProvider(props: any) { | ||||
|   return ( | ||||
|     <UserinfoContext.Provider value={userinfo}> | ||||
|       {props.children} | ||||
|     </UserinfoContext.Provider> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function useUserinfo() { | ||||
|   return useContext(UserinfoContext); | ||||
| } | ||||
							
								
								
									
										23
									
								
								pkg/view/src/stores/wellKnown.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								pkg/view/src/stores/wellKnown.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import { createContext, useContext } from "solid-js"; | ||||
| import { createStore } from "solid-js/store"; | ||||
|  | ||||
| const WellKnownContext = createContext<any>(); | ||||
|  | ||||
| const [wellKnown, setWellKnown] = createStore<any>(null); | ||||
|  | ||||
| export async function readWellKnown() { | ||||
|   const res = await fetch("/.well-known") | ||||
|   setWellKnown(await res.json()) | ||||
| } | ||||
|  | ||||
| export function WellKnownProvider(props: any) { | ||||
|   return ( | ||||
|     <WellKnownContext.Provider value={wellKnown}> | ||||
|       {props.children} | ||||
|     </WellKnownContext.Provider> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function useWellKnown() { | ||||
|   return useContext(WellKnownContext); | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/view/src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/view/src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| /// <reference types="vite/client" /> | ||||
		Reference in New Issue
	
	Block a user