diff --git a/db/.editorconfig b/db/.editorconfig new file mode 100644 index 0000000..09d7a33 --- /dev/null +++ b/db/.editorconfig @@ -0,0 +1,11 @@ +# https://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/db/.eslintrc.js b/db/.eslintrc.js new file mode 100644 index 0000000..f845b10 --- /dev/null +++ b/db/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ["blitz"], +} diff --git a/db/.gitignore b/db/.gitignore new file mode 100644 index 0000000..f6fda81 --- /dev/null +++ b/db/.gitignore @@ -0,0 +1,53 @@ +# dependencies +node_modules +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.pnp.* +.npm +web_modules/ + +# blitz +/.blitz/ +/.next/ +*.sqlite +*.sqlite-journal +.now +.blitz** +blitz-log.log + +# misc +.DS_Store + +# local env files +.env.local +.env.*.local +.envrc + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Testing +.coverage +*.lcov +.nyc_output +lib-cov + +# Caches +*.tsbuildinfo +.eslintcache +.node_repl_history +.yarn-integrity + +# Serverless directories +.serverless/ + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test diff --git a/db/.npmrc b/db/.npmrc new file mode 100644 index 0000000..f7777f0 --- /dev/null +++ b/db/.npmrc @@ -0,0 +1,7 @@ +save-exact=true +legacy-peer-deps=true + +public-hoist-pattern[]=next +public-hoist-pattern[]=secure-password +public-hoist-pattern[]=*jest* +public-hoist-pattern[]=@testing-library/* diff --git a/db/.prettierignore b/db/.prettierignore new file mode 100644 index 0000000..ae16525 --- /dev/null +++ b/db/.prettierignore @@ -0,0 +1,10 @@ +.gitkeep +.env* +*.ico +*.lock +.next +.blitz +.yarn +.pnp.* +node_modules +.blitz.config.compiled.js diff --git a/db/README.md b/db/README.md new file mode 100644 index 0000000..35b4a62 --- /dev/null +++ b/db/README.md @@ -0,0 +1,117 @@ +[![Blitz.js](https://raw.githubusercontent.com/blitz-js/art/master/github-cover-photo.png)](https://blitzjs.com) + +This is a minimal [Blitz.js](https://github.com/blitz-js/blitz) app. + +# **db** + +## Getting Started + +Run your app in the development mode. + +``` +yarn blitz p generate +yarn blitz dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +## Tests + +Runs your tests using Jest. + +``` +yarn test +``` + +Blitz comes with a test setup using [Jest](https://jestjs.io/) and [react-testing-library](https://testing-library.com/). + +## Commands + +Blitz comes with a powerful CLI that is designed to make development easy and fast. You can install it with `npm i -g blitz` + +``` + blitz [COMMAND] + + dev Start a development server + build Create a production build + start Start a production server + export Export your Blitz app as a static application + prisma Run prisma commands + generate Generate new files for your Blitz project + console Run the Blitz console REPL + install Install a recipe + help Display help for blitz + test Run project tests +``` + +You can read more about it on the [CLI Overview](https://blitzjs.com/docs/cli-overview) documentation. + +## What's included? + +Here is the starting structure of your app. + +``` +db +├── app/ +│ ├── pages/ +│ │ ├── _app.tsx +│ │ ├── _document.tsx +│ │ ├── 404.tsx +│ │ ├── index.test.tsx +│ │ └── index.tsx +├── public/ +│ ├── favicon.ico +│ └── logo.png +├── test/ +│ ├── setup.ts +│ └── utils.tsx +├── .eslintrc.js +├── babel.config.js +├── blitz.config.ts +├── jest.config.ts +├── package.json +├── README.md +├── tsconfig.json +└── types.ts +``` + +These files are: + +- The `app/` folder is a container for most of your project. This is where you’ll put any pages or API routes. + +- `public/` is a folder where you will put any static assets. If you have images, files, or videos which you want to use in your app, this is where to put them. + +- `test/` is a folder where you can put test utilities and integration tests. + +- `package.json` contains information about your dependencies and devDependencies. If you’re using a tool like `npm` or `yarn`, you won’t have to worry about this much. + +- `tsconfig.json` is our recommended setup for TypeScript. + +- `.babel.config.js`, `.eslintrc.js`, `.env`, etc. ("dotfiles") are configuration files for various bits of JavaScript tooling. + +- `blitz.config.ts` is for advanced custom configuration of Blitz. [Here you can learn how to use it](https://blitzjs.com/docs/blitz-config). + +- `jest.config.js` contains config for Jest tests. You can [customize it if needed](https://jestjs.io/docs/en/configuration). + +You can read more about it in the [File Structure](https://blitzjs.com/docs/file-structure) section of the documentation. + +### Tools included + +Blitz comes with a set of tools that corrects and formats your code, facilitating its future maintenance. You can modify their options and even uninstall them. + +- **ESLint**: It lints your code: searches for bad practices and tell you about it. You can customize it via the `.eslintrc.js`, and you can install (or even write) plugins to have it the way you like it. It already comes with the [`blitz`](https://github.com/blitz-js/blitz/tree/canary/packages/eslint-config) config, but you can remove it safely. [Learn More](https://blitzjs.com/docs/eslint-config). +- **Husky**: It adds [githooks](https://git-scm.com/docs/githooks), little pieces of code that get executed when certain Git events are triggerd. For example, `pre-commit` is triggered just before a commit is created. You can see the current hooks inside `.husky/`. If are having problems commiting and pushing, check out ther [troubleshooting](https://typicode.github.io/husky/#/?id=troubleshoot) guide. [Learn More](https://blitzjs.com/docs/husky-config). +- **Prettier**: It formats your code to look the same everywhere. You can configure it via the `.prettierrc` file. The `.prettierignore` contains the files that should be ignored by Prettier; useful when you have large files or when you want to keep a custom formatting. [Learn More](https://blitzjs.com/docs/prettier-config). + +## Learn more + +Read the [Blitz.js Documentation](https://blitzjs.com/docs/getting-started) to learn more. + +The Blitz community is warm, safe, diverse, inclusive, and fun! Feel free to reach out to us in any of our communication channels. + +- [Website](https://blitzjs.com) +- [Discord](https://blitzjs.com/discord) +- [Report an issue](https://github.com/blitz-js/blitz/issues/new/choose) +- [Forum discussions](https://github.com/blitz-js/blitz/discussions) +- [How to Contribute](https://blitzjs.com/docs/contributing) +- [Sponsor or donate](https://github.com/blitz-js/blitz#sponsors-and-donations) diff --git a/db/app/api/greetingsQueue.ts b/db/app/api/greetingsQueue.ts new file mode 100644 index 0000000..b96fd93 --- /dev/null +++ b/db/app/api/greetingsQueue.ts @@ -0,0 +1,13 @@ +import { Queue } from "quirrel/blitz" + +export interface Greetings { + to: string + message: string +} + +export default Queue( + "api/greetingsQueue", // the path of this API route + async ({ to, message }: Greetings) => { + console.log(`Greetings, ${to}! Thy ears shall hear: "${message}"`) + } +) diff --git a/db/app/api/items/refresh.ts b/db/app/api/items/refresh.ts new file mode 100644 index 0000000..96b7960 --- /dev/null +++ b/db/app/api/items/refresh.ts @@ -0,0 +1,34 @@ +import { CronJob } from "quirrel/blitz" +import { PrismaClient, Item } from '@prisma/client'; +import { RawItemData } from '../../core/dto/rawData/RawItemData'; +import { propsToClassKey } from "@mui/styles"; + +export default CronJob( + "api/items/refresh", // the path of this API route + "@daily", // cron schedule (see https://crontab.guru) + async () => { + const itemsResponse = await fetch('https://dev.sp-tarkov.com/SPT-AKI/Server/raw/branch/development/project/assets/database/templates/items.json'); + if (itemsResponse.status >= 300) { + throw Error(`Could not retrieve items from Gitea: code ${itemsResponse.status}`) + } + const items: { [id: string]: RawItemData } = await itemsResponse.json(); + const prisma: PrismaClient = new PrismaClient(); + + const promises: any[] = [] + + Object.entries(items).forEach( + async ([key, value]) => { + const {props, ...data} = RawItemData.fromRawData(value).toItemData(); + promises.push(await prisma.item.upsert({ + create: {props, ...data}, + update: data, + where: { + internalId: data.internalId + }, + })) + } + ); + + await Promise.all(promises); + } +) diff --git a/db/app/core/components/DarkModeToggle.tsx b/db/app/core/components/DarkModeToggle.tsx new file mode 100644 index 0000000..acd5feb --- /dev/null +++ b/db/app/core/components/DarkModeToggle.tsx @@ -0,0 +1,68 @@ +import {useTheme as nextUseTheme} from "next-themes" +import { Box, IconButton, Theme, Typography } from '@mui/material'; +import { useTheme } from '@mui/material/styles' +import Brightness4Icon from '@mui/icons-material/Brightness4' +import Brightness7Icon from '@mui/icons-material/Brightness7' +import { ThemeMode } from '../state/ThemeMode' +import { useGlobalState } from '../state/GlobalState' +import { useCallback, useEffect, useState } from 'react' +import { makeStyles } from '@mui/styles' + +const useStyles = makeStyles((theme: Theme) => ({ + modeToggleButtonHolder: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flexGrow: 1, + color: theme.palette.text.primary, + }, + iconButton: { + ml: 1, + }, +})) + +export const DarkModeToggle = () => { + const [mounted, setMounted] = useState(false) + const theme = useTheme() + const {setTheme} = nextUseTheme() + const classes = useStyles() + const [preferedColorScheme, setPreferedColorScheme] = useGlobalState( + useCallback( + (state) => [state.preferedColorScheme, state.setPreferedColorScheme], + [], + ), + ) + + useEffect(() => setMounted(true), []) + if (!mounted) return null + + + const toggleColor = () => { + const newTheme = + preferedColorScheme === ThemeMode.LIGHT_MODE + ? ThemeMode.DARK_MODE + : ThemeMode.LIGHT_MODE + setTheme(newTheme); + setPreferedColorScheme(newTheme) + } + + + return ( + + {theme.palette.mode} toggle + + {theme.palette.mode === 'dark' ? ( + + ) : ( + + )} + + + ) +} diff --git a/db/app/core/components/Footer.tsx b/db/app/core/components/Footer.tsx new file mode 100644 index 0000000..bc59ce4 --- /dev/null +++ b/db/app/core/components/Footer.tsx @@ -0,0 +1,25 @@ +import {Box, Theme, Typography} from '@mui/material' +import {makeStyles} from '@mui/styles' + +const useStyles = makeStyles((theme: Theme) => ({ + footerHolder: { + display: 'flex', + flex: '0 1 3vh', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + padding: '0 10vw 0 10vw', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary + } +})) + +export const Footer = () => { + const classes = useStyles() + + return ( + + SPT-Aki ©2021 Created by Rev and Shirito + + ) +} diff --git a/db/app/core/components/Form.tsx b/db/app/core/components/Form.tsx new file mode 100644 index 0000000..133802d --- /dev/null +++ b/db/app/core/components/Form.tsx @@ -0,0 +1,53 @@ +import { ReactNode, PropsWithoutRef } from "react" +import { Form as FinalForm, FormProps as FinalFormProps } from "react-final-form" +import { z } from "zod" +import { validateZodSchema } from "blitz" +export { FORM_ERROR } from "final-form" + +export interface FormProps> + extends Omit, "onSubmit"> { + /** All your form fields */ + children?: ReactNode + /** Text to display in the submit button */ + submitText?: string + schema?: S + onSubmit: FinalFormProps>["onSubmit"] + initialValues?: FinalFormProps>["initialValues"] +} + +export function Form>({ + children, + submitText, + schema, + initialValues, + onSubmit, + ...props +}: FormProps) { + return ( + ( +
+ {/* Form fields supplied as children are rendered here */} + {children} + + {submitError && ( +
+ {submitError} +
+ )} + + {submitText && ( + + )} +
+ )} + /> + ) +} + +export default Form \ No newline at end of file diff --git a/db/app/core/components/Header.tsx b/db/app/core/components/Header.tsx new file mode 100644 index 0000000..6241566 --- /dev/null +++ b/db/app/core/components/Header.tsx @@ -0,0 +1,87 @@ +import { Box, Link, Theme } from '@mui/material' +import { makeStyles } from '@mui/styles' +import { useCallback } from 'react'; +import { useGlobalState } from '../state/GlobalState'; +import { HeaderForm } from './HeaderForm'; + +const useStyles = makeStyles((theme: Theme) => ({ + headerContainer: { + display: 'flex', + flex: '0 1 3vh', + flexDirection: 'row', + backgroundColor: theme.palette.background.paper, + alignItems: 'center', + padding: '0 10vw 0 10vw', + }, + linksContainer: { + display: 'flex', + flexGrow: 2, + flexDirection: 'row', + alignItems: 'center', + height: '100%', + }, + formContainer: { + display: 'flex', + flexGrow: 1, + flexDirection: 'row', + alignItems: 'center', + height: '100%', + }, + link: { + display: 'flex', + padding: '0 1vw 0 1vw', + height: '100%', + alignItems: 'center', + borderBottom: `1px solid transparent`, + color: theme.palette.text.primary, + '&:hover': { + borderBottom: `1px solid ${theme.palette.action.hover}`, + }, + }, +})) + +export const Header = () => { + const classes = useStyles() + const websiteLink = useGlobalState(useCallback((state) => state.sptarkovWebsiteUrl,[])) + const workshopLink = useGlobalState(useCallback((state) => state.sptarkovWorkshopUrl,[])) + const documentationLink = useGlobalState(useCallback((state) => state.sptarkovDocumentationUrl,[])) + + return ( + <> + + + + Website + + + Workshop + + + Documentation + + + + + + + + ) +} diff --git a/items/frontend/src/components/HeaderForm.tsx b/db/app/core/components/HeaderForm.tsx similarity index 100% rename from items/frontend/src/components/HeaderForm.tsx rename to db/app/core/components/HeaderForm.tsx diff --git a/db/app/core/components/JsonTheme.tsx b/db/app/core/components/JsonTheme.tsx new file mode 100644 index 0000000..5ff19fb --- /dev/null +++ b/db/app/core/components/JsonTheme.tsx @@ -0,0 +1,74 @@ +import { + Box, + FormControl, + MenuItem, + Select, + Theme, +} from '@mui/material' +import { makeStyles } from '@mui/styles' +import { ReactJsonViewThemes } from '../state/ReactJsonViewThemes' +import { LocalStorageKeys } from '../dto/SaveKeys' +import { useGlobalState } from '../state/GlobalState' +import { useCallback, useEffect, useState } from 'react'; + +const useStyles = makeStyles((theme: Theme) => ({ + jsonHolder: { + display: 'flex', + flexGrow: 1, + padding: '0 0.5vw 0 0.5vw' + }, + select: { + display: 'flex', + flexGrow: 1 + } +})) + +export const JsonTheme = () => { + const [mounted, setMounted] = useState(false) + const classes = useStyles() + const [preferedJsonViewerTheme, setPreferedJsonViewerTheme] = useGlobalState( + useCallback( + (state) => [ + state.preferedJsonViewerTheme, + state.setPreferedJsonViewerTheme, + ], + [], + ), + ) + + useEffect(() => setMounted(true), []) + if (!mounted) return null + + return ( + <> + + + + + + + ) +} diff --git a/db/app/core/components/LocaleSelect.tsx b/db/app/core/components/LocaleSelect.tsx new file mode 100644 index 0000000..1c2acfe --- /dev/null +++ b/db/app/core/components/LocaleSelect.tsx @@ -0,0 +1,58 @@ +import { Select, MenuItem, Theme, Box, FormControl } from '@mui/material' +import {makeStyles} from '@mui/styles' +import { useCallback, useEffect, useState } from 'react'; +import { useGlobalState } from '../state/GlobalState' + +const useStyles = makeStyles((theme: Theme) => ({ + localeHolder: { + display: 'flex', + flexGrow: 1, + padding: '0 0.5vw 0 0.5vw' + }, + select: { + display: 'flex', + flexGrow: 1 + } +})) + + + +export const LocaleSelect = () => { + const [mounted, setMounted] = useState(false) + const classes = useStyles() + const [preferedLocale, setPreferedLocale] = useGlobalState(useCallback(state => [state.preferedLocale, state.setPreferedLocale],[])) + const [localesList, refreshLocalesList] = useGlobalState(useCallback(state => [state.localesList, state.refreshLocalesList],[])) + + useEffect(() => setMounted(true), []) + useEffect(()=> {refreshLocalesList();}, [refreshLocalesList]) + + if (!mounted) return null + + return ( + <> + + + + + + + ) +} diff --git a/db/app/core/components/NavigationBreadcrumb.tsx b/db/app/core/components/NavigationBreadcrumb.tsx new file mode 100644 index 0000000..452a3ec --- /dev/null +++ b/db/app/core/components/NavigationBreadcrumb.tsx @@ -0,0 +1,111 @@ +import { useState } from 'react' +import { Box, Breadcrumbs, Link, Theme, Typography } from '@mui/material' +import { makeStyles } from '@mui/styles' +import { useEffect } from 'react' +import { useGlobalState } from '../state/GlobalState' +import { ItemWithLocale } from '../dto/ItemWithLocale' + +const useStyles = makeStyles((theme: Theme) => ({ + breadcrumbHolder: { + display: 'flex', + flex: '0 1 3vh', + flexDirection: 'row', + alignItems: 'center', + padding: '0 10vw 0 10vw', + borderBottom: `1px solid ${theme.palette.background.paper}`, + }, + breadcrumb: { + display: 'flex', + flex: '0 1 3vh', + flexDirection: 'row', + flexGrow: 1, + }, + link: { + color: theme.palette.text.secondary, + display: 'flex', + padding: '0 1vw 0 1vw', + height: '100%', + alignItems: 'center', + borderBottom: `1px solid transparent`, + '&:hover': { + color: theme.palette.action.hover, + cursor: 'pointer', + }, + }, + currentItem: { + cursor: 'default', + borderBottom: `1px solid ${theme.palette.action.hover}`, + }, +})) + +export const NavigationBreadcrumb = () => { + const classes = useStyles() + const setSelectedItem = useGlobalState((state) => state.setSelectedItem) + const itemHierachyState = useGlobalState((state) => state.itemsHierarchy) + const [searchInputState, setSearchInput] = useGlobalState((state) => [ + state.searchInput, + state.setSearchInput, + ]) + const selectedItem = useGlobalState((state) => state.selectedItem) + const [currentHierarchy, setCurrentHierarchy] = useState([]) + + useEffect(() => { + if (!selectedItem) return; + + const hierarchy: ItemWithLocale[] = [selectedItem] + let currItemID: string | undefined = selectedItem?.item?._parent + while (currItemID) { + const item: ItemWithLocale = itemHierachyState[currItemID]! + hierarchy.push(item) + currItemID = item?.item?._parent + } + setCurrentHierarchy(hierarchy.filter(elt => elt !== undefined && elt !== null).reverse()) + }, [selectedItem, itemHierachyState]) + + const formatLink = (item: ItemWithLocale, idx: string) => { + if ( + searchInputState === item.locale.Name || + searchInputState === item.item._id || + searchInputState === item.item._name + ) { + return ( + + {item.locale.Name ? item.locale.Name : item.item._name} + + ) + } else { + return ( + { + setSearchInput(item.item._id) + setSelectedItem(undefined) + }} + className={classes.link} + > + {item.locale.Name ? item.locale.Name : item.item._name} + + ) + } + } + + return ( + + + + Home + + {currentHierarchy.map((item, idx) => formatLink(item, idx.toString()))} + + + ) +} diff --git a/db/app/core/components/SearchArea.tsx b/db/app/core/components/SearchArea.tsx new file mode 100644 index 0000000..0b98af9 --- /dev/null +++ b/db/app/core/components/SearchArea.tsx @@ -0,0 +1,207 @@ +import {ReactElement, SyntheticEvent, useCallback, useEffect, useState} from 'react' +import {Autocomplete, Box, CircularProgress, Theme, Typography,} from '@mui/material' +import {makeStyles} from '@mui/styles' +import TextField from '@mui/material/TextField' +import {getItem, getItemHierarchy, searchItem} from '../dataaccess/ItemBackend'; +import {ItemOption} from '../dto/ItemOption' +import {useGlobalState} from '../state/GlobalState' +// import {useNavigate, useParams} from "react-router-dom"; +import {useRouter, dynamic } from "blitz"; + +let ReactJson +if (process.browser){ + ReactJson = dynamic(() => import("react-json-view")) +} + +interface IItemOption { + id: string + name: string + shortName?: string +} + +const useStyles = makeStyles((theme: Theme) => ({ + searchAreaHolder: { + display: 'flex', + flexGrow: 1, + flexDirection: 'column', + background: theme.palette.background.paper, + padding: '2vh 2vw 2vh 2vw', + }, + jsonHolder: { + display: 'flex', + flexGrow: 1, + alignItems: 'center', + flexDirection: 'column', + background: theme.palette.background.paper, + maxHeight: '80vh', + }, + autocomplete: {}, +})) + +export const SearchArea = () => { + const classes = useStyles(); + const router = useRouter() + const params = router.query; + const preferedLocale = useGlobalState((state) => state.preferedLocale) + const preferedJsonViewerTheme = useGlobalState( + useCallback((state) => state.preferedJsonViewerTheme, []), + ) + const [searchInputState, setSearchInput] = useGlobalState((state) => [ + state.searchInput, + state.setSearchInput, + ]) + const [setHierarchy, initHierarchy] = useGlobalState((state) => [state.setHierarchy, state.initHierarchy]) + const [selectedItem, setSelectedItem] = useGlobalState((state) => [ + state.selectedItem, + state.setSelectedItem, + ]) + const [selectOptions, setSelecteOptions] = useState([]) + const [isbusy, setIsBusy] = useState(false) + const searchThreshold = 3 + + const handleNameInput = useCallback(async (input: string) => { + const searchResults = await searchItem(input, preferedLocale) + const options = searchResults?.map((res) => ({ + id: res.item._id, + name: res.locale.Name ? res.locale.Name : res.item._name, + shortName: JSON.stringify(res.locale.ShortName) + })) + setSelecteOptions(options ? options : []) + }, [preferedLocale]) + + const handleIDInput = useCallback(async (input: string) => { + const itemJson = await getItem(input, preferedLocale) + if (!itemJson) { + setSelectedItem(undefined) + setSearchInput('') + return; + } + + setSelectedItem(itemJson) + const itemObj = { + id: itemJson.item._id, + name: itemJson.locale.Name ? itemJson.locale.Name : itemJson.item._name, + shortName: itemJson.locale.ShortName + } + setSelecteOptions([itemObj]) + setSearchInput(itemObj.name) + + // Update hierachy + const itemHierarchy = await getItemHierarchy( + itemJson.item, + preferedLocale, + ) + setHierarchy(itemHierarchy ? itemHierarchy : {}) + // eslint-disable-next-line + }, []) // Need to only be created on startup + + useEffect(() => initHierarchy(), [initHierarchy]) + + useEffect(()=>{ + if (selectedItem){ + router.replace(`/search/${selectedItem.item._id}`) + } + },[selectedItem, router]) + + useEffect(() => { + if (searchInputState && searchInputState.match(/([a-z0-9]{24})/)) { + handleIDInput(searchInputState) + } + }, [handleIDInput, searchInputState]) + + const handleInput = useCallback(async (input: string) => { + if (!input || input.length < searchThreshold || isbusy) { + setSelectedItem(undefined) + setSelecteOptions([]) + setIsBusy(false) + return + } + setIsBusy(true) + + if (input.match(/([a-z0-9]{24})/)) await handleIDInput(input) + else await handleNameInput(input) + + setIsBusy(false) + }, [handleIDInput, handleNameInput, isbusy, setSelectedItem]) + + useEffect(() => { + if (!searchInputState && params.id) { + const newId = (params.id as string).trim(); + console.log(newId); + setSearchInput(newId); + (async () => await handleInput(newId))(); + } + }, [params, searchInputState, setSearchInput, handleInput, router]) + + const formatDisplayItems = () => { + // If loading + if (isbusy) return + + // If finished loading + console.log(process.browser) + if (selectedItem && ReactJson !== undefined) { + return () + } else return No data to display + // return No data to display + } + + const findOptionValue = (option: ItemOption, value: ItemOption): boolean => { + return option.name?.toLocaleLowerCase() === value.name?.toLocaleLowerCase() + || option.id?.toLocaleLowerCase() === value.id?.toLocaleLowerCase() + || option.shortName?.toLocaleLowerCase() === value.shortName?.toLocaleLowerCase() + } + + return ( + + ({name: elt.name, shortName: elt.shortName, id: elt.id}))} + getOptionLabel={(option) => (option.name ? option.name : option.id) as string} + isOptionEqualToValue={(option, value) => findOptionValue(option, value)} + open={!isbusy && searchInputState.length >= searchThreshold && (searchInputState !== selectedItem?.locale.Name && searchInputState !== selectedItem?.item._name)} + className={classes.autocomplete} + inputValue={searchInputState ? searchInputState : ''} + onInputChange={async (evt: SyntheticEvent, newValue: string) => { + if (!evt) return + setSelectedItem(undefined) + setSearchInput(newValue) + await handleInput(newValue.trim()) + }} + value={(() => { + const selectedOption = selectOptions.find(elt => elt.id === searchInputState || elt.name === searchInputState); + return selectedOption ? selectedOption : null; + })()} + onChange={async (event: SyntheticEvent, newValue: IItemOption | null) => { + if (newValue) { + const selectedOption = selectOptions.find( + (elt) => elt.name === newValue.name, + ) + if (selectedOption) await handleIDInput(selectedOption.id) + } + }} + renderInput={(params) => ( + + )} + renderOption={(props, option ) => ( +
  • {option.name}
  • + )} + filterOptions={(options, state) => options.filter(elt => { + return (elt.name?.toLocaleLowerCase().includes(state.inputValue.toLocaleLowerCase()) + || elt.id?.toLocaleLowerCase().includes(state.inputValue.toLocaleLowerCase()) + || elt.shortName?.toLocaleLowerCase().includes(state.inputValue.toLocaleLowerCase())) + })} + filterSelectedOptions + /> + {formatDisplayItems()} +
    + ) +} diff --git a/db/app/core/dataaccess/ItemBackend.ts b/db/app/core/dataaccess/ItemBackend.ts new file mode 100644 index 0000000..78cc07a --- /dev/null +++ b/db/app/core/dataaccess/ItemBackend.ts @@ -0,0 +1,14 @@ +export const getItem = (test:any,test2:any):any=>{return null} +export const getItemHierarchy = (test:any,test2:any):any =>{return null} +export const searchItem = (test:any,test2:any):any[] =>{ return [ + { + item:{ + _id: 'ABCDE', + _name: 'Name', + }, + locale:{ + name: "test" + } + + } +]} \ No newline at end of file diff --git a/db/app/core/dto/ItemData.ts b/db/app/core/dto/ItemData.ts new file mode 100644 index 0000000..511f0d4 --- /dev/null +++ b/db/app/core/dto/ItemData.ts @@ -0,0 +1,59 @@ +export interface ItemData { + _id: string + _name: string + _parent?: string + _type?: string + _props: { + Name: string + ShortName: string + Description: string + Weight: number + BackgroundColor: string + Width: number + Height: number + StackMaxSize: number + ItemSound: string + Prefab: { + path: string + rcid: string + } + UsePrefab: { + path: string + rcid: string + } + StackObjectsCount: number + NotShownInSlot: boolean + ExaminedByDefault: boolean + ExamineTime: number + IsUndiscardable: boolean + IsUnsaleable: boolean + IsUnbuyable: boolean + IsUngivable: boolean + IsLockedafterEquip: boolean + QuestItem: boolean + LootExperience: number + ExamineExperience: number + HideEntrails: boolean + RepairCost: number + RepairSpeed: number + ExtraSizeLeft: number + ExtraSizeRight: number + ExtraSizeUp: number + ExtraSizeDown: number + ExtraSizeForceAdd: boolean + MergesWithChildren: boolean + CanSellOnRagfair: boolean + CanRequireOnRagfair: boolean + ConflictingItems: string[] + Unlootable: boolean + UnlootableFromSlot: string + UnlootableFromSide: string[] + AnimationVariantsNumber: number + DiscardingBlock: boolean + RagFairCommissionModifier: number + IsAlwaysAvailableForInsurance: boolean + MaxResource: number + Resource: number + } + _proto?: string +} \ No newline at end of file diff --git a/items/frontend/src/dto/ItemHierarchy.ts b/db/app/core/dto/ItemHierarchy.ts similarity index 100% rename from items/frontend/src/dto/ItemHierarchy.ts rename to db/app/core/dto/ItemHierarchy.ts diff --git a/items/frontend/src/dto/ItemLocale.ts b/db/app/core/dto/ItemLocale.ts similarity index 100% rename from items/frontend/src/dto/ItemLocale.ts rename to db/app/core/dto/ItemLocale.ts diff --git a/items/frontend/src/dto/ItemOption.ts b/db/app/core/dto/ItemOption.ts similarity index 100% rename from items/frontend/src/dto/ItemOption.ts rename to db/app/core/dto/ItemOption.ts diff --git a/items/frontend/src/dto/ItemWithLocale.ts b/db/app/core/dto/ItemWithLocale.ts similarity index 100% rename from items/frontend/src/dto/ItemWithLocale.ts rename to db/app/core/dto/ItemWithLocale.ts diff --git a/items/frontend/src/dataaccess/SaveKeys.ts b/db/app/core/dto/SaveKeys.ts similarity index 100% rename from items/frontend/src/dataaccess/SaveKeys.ts rename to db/app/core/dto/SaveKeys.ts diff --git a/db/app/core/dto/rawData/RawItemData.ts b/db/app/core/dto/rawData/RawItemData.ts new file mode 100644 index 0000000..c48b0c7 --- /dev/null +++ b/db/app/core/dto/rawData/RawItemData.ts @@ -0,0 +1,29 @@ +import { Prisma } from '@prisma/client'; +import { RawItemProps } from './RawItemProps'; + +export class RawItemData { + _id: string; + _name: string; + _parent: string; + _type: string; + _props: RawItemProps; + _proto?: string | undefined; + + static fromRawData(data: any): RawItemData { + const rawData = new RawItemData(); + Object.assign(rawData, data) + rawData._props = RawItemProps.fromRawData(data['_props']) + + return rawData; + } + + toItemData(): Prisma.XOR { + return { + internalId: this._id, + name: this._name, + type: this._type, + props: this._props.toItemPropsData(), + proto: this._proto + } + } +} \ No newline at end of file diff --git a/db/app/core/dto/rawData/RawItemPrefab.ts b/db/app/core/dto/rawData/RawItemPrefab.ts new file mode 100644 index 0000000..8121853 --- /dev/null +++ b/db/app/core/dto/rawData/RawItemPrefab.ts @@ -0,0 +1,27 @@ +import { Prisma } from "@prisma/client" + +export class RawItemPrefab { + id: number + path: string + rcid: string + + static fromRawData(data: any): RawItemPrefab { + const rawData = new RawItemPrefab(); + Object.assign(rawData, data) + return rawData + } + + toItemPrefabData(): Prisma.ItemPrefabCreateNestedOneWithoutItemPropInput { + return { + connectOrCreate: { + where: { + id: this.id + }, + create: { + path: this.path, + rcid: this.path + } + } + } + } +} \ No newline at end of file diff --git a/db/app/core/dto/rawData/RawItemProps.ts b/db/app/core/dto/rawData/RawItemProps.ts new file mode 100644 index 0000000..b1a2085 --- /dev/null +++ b/db/app/core/dto/rawData/RawItemProps.ts @@ -0,0 +1,77 @@ +import { Prisma } from '@prisma/client'; +import { RawItemPrefab } from './RawItemPrefab'; +import { RawItemUsePrefab } from './RawItemUsePrefab'; + +export class RawItemProps { + id: number + Name: string + ShortName: string + Description: string + Weight: number + BackgroundColor: string + Width: number + Height: number + StackMaxSize: number + ItemSound: string + Prefab: RawItemPrefab + UsePrefab: RawItemUsePrefab + StackObjectsCount: number + NotShownInSlot: boolean + ExaminedByDefault: boolean + ExamineTime: number + IsUndiscardable: boolean + IsUnsaleable: boolean + IsUnbuyable: boolean + IsUngivable: boolean + IsLockedafterEquip: boolean + QuestItem: boolean + LootExperience: number + ExamineExperience: number + HideEntrails: boolean + RepairCost: number + RepairSpeed: number + ExtraSizeLeft: number + ExtraSizeRight: number + ExtraSizeUp: number + ExtraSizeDown: number + ExtraSizeForceAdd: boolean + MergesWithChildren: boolean + CanSellOnRagfair: boolean + CanRequireOnRagfair: boolean + ConflictingItems: string[] + Unlootable: boolean + UnlootableFromSlot: string + UnlootableFromSide: string[] + AnimationVariantsNumber: number + DiscardingBlock: boolean + RagFairCommissionModifier: number + IsAlwaysAvailableForInsurance: boolean + MaxResource: number + Resource: number + Backgroundcolor: string + IsLockedAfterEquip: boolean + + static fromRawData(data: any): RawItemProps { + const rawData = new RawItemProps(); + Object.assign(rawData, data) + rawData.Prefab = RawItemPrefab.fromRawData(data['Prefab']) + rawData.UsePrefab = RawItemUsePrefab.fromRawData(data['UsePrefab']) + return rawData + } + + toItemPropsData(): Prisma.ItemPropCreateNestedOneWithoutItemInput { + const {id, ...data} = this; + return { + connectOrCreate: { + where: { + id + }, + create: { + ...data, + Prefab: this.Prefab.toItemPrefabData(), + UsePrefab: this.UsePrefab.toItemUsePrefabData() + } + } + } + } +} \ No newline at end of file diff --git a/db/app/core/dto/rawData/RawItemUsePrefab.ts b/db/app/core/dto/rawData/RawItemUsePrefab.ts new file mode 100644 index 0000000..e83cb8f --- /dev/null +++ b/db/app/core/dto/rawData/RawItemUsePrefab.ts @@ -0,0 +1,26 @@ +import { Prisma } from '@prisma/client'; +export class RawItemUsePrefab { + id: number + path: string + rcid: string + + static fromRawData(data: any): RawItemUsePrefab { + const rawData = new RawItemUsePrefab(); + Object.assign(rawData, data) + return rawData + } + + toItemUsePrefabData(): Prisma.ItemUsePrefabCreateNestedOneWithoutItemPropInput { + return { + connectOrCreate: { + where: { + id: this.id + }, + create: { + path: this.path, + rcid: this.rcid + } + } + } + } +} \ No newline at end of file diff --git a/db/app/core/layouts/Layout.tsx b/db/app/core/layouts/Layout.tsx new file mode 100644 index 0000000..21837bb --- /dev/null +++ b/db/app/core/layouts/Layout.tsx @@ -0,0 +1,35 @@ +import {Box} from '@mui/material' +import {Footer} from '../components/Footer' +import {Header} from '../components/Header' +import {makeStyles} from "@mui/styles"; +import React, { PropsWithChildren } from "react"; +// import {InteractiveArea} from "./InteractiveArea"; +// import {PageNotFound} from "./PageNotFound"; + +const useStyles = makeStyles(() => ({ + container: { + background: 'background.default', + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + height: '100vh', + maxheight: '100vh', + } +})) + +interface Props {} + +export const Layout = (props: PropsWithChildren) => { + const classes = useStyles(); + return ( + <> + +
    + {props.children} +