WIP: feat: Create new db app #32

Closed
Ghost wants to merge 6 commits from feat/migrate-to-blitzjs into development
214 changed files with 14076 additions and 0 deletions

11
db/.editorconfig Normal file
View File

@ -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

3
db/.eslintrc.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
extends: ["blitz"],
}

53
db/.gitignore vendored Normal file
View File

@ -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

7
db/.npmrc Normal file
View File

@ -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/*

10
db/.prettierignore Normal file
View File

@ -0,0 +1,10 @@
.gitkeep
.env*
*.ico
*.lock
.next
.blitz
.yarn
.pnp.*
node_modules
.blitz.config.compiled.js

117
db/README.md Normal file
View File

@ -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 youll 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 youre using a tool like `npm` or `yarn`, you wont 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)

View File

@ -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}"`)
}
)

View File

@ -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);
}
)

View File

@ -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 (
<Box className={classes.modeToggleButtonHolder} id="modeToggleButtonHolder">
<Typography>{theme.palette.mode} toggle</Typography>
<IconButton
className={classes.iconButton}
sx={{ ml: 1 }}
onClick={toggleColor}
color="inherit"
id="modeToggleButton"
>
{theme.palette.mode === 'dark' ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
</Box>
)
}

View File

@ -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 (
<Box className={classes.footerHolder}>
<Typography id={"footer"}>SPT-Aki ©2021 Created by Rev and Shirito</Typography>
</Box>
)
}

View File

@ -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<S extends z.ZodType<any, any>>
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
/** All your form fields */
children?: ReactNode
/** Text to display in the submit button */
submitText?: string
schema?: S
onSubmit: FinalFormProps<z.infer<S>>["onSubmit"]
initialValues?: FinalFormProps<z.infer<S>>["initialValues"]
}
export function Form<S extends z.ZodType<any, any>>({
children,
submitText,
schema,
initialValues,
onSubmit,
...props
}: FormProps<S>) {
return (
<FinalForm
initialValues={initialValues}
validate={validateZodSchema(schema)}
onSubmit={onSubmit}
render={({ handleSubmit, submitting, submitError }) => (
<form onSubmit={handleSubmit} className="form" {...props}>
{/* Form fields supplied as children are rendered here */}
{children}
{submitError && (
<div role="alert" style={{ color: "red" }}>
{submitError}
</div>
)}
{submitText && (
<button type="submit" disabled={submitting}>
{submitText}
</button>
)}
</form>
)}
/>
)
}
export default Form

View File

@ -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 (
<>
<Box className={classes.headerContainer}>
<Box className={classes.linksContainer}>
<Link
underline="hover"
color="inherit"
id="website-link"
href={websiteLink}
className={classes.link}
>
Website
</Link>
<Link
underline="hover"
color="inherit"
id="workshop-link"
href={workshopLink}
className={classes.link}
>
Workshop
</Link>
<Link
underline="hover"
color="inherit"
id="documentation-link"
href={documentationLink}
className={classes.link}
>
Documentation
</Link>
</Box>
<Box className={classes.formContainer}>
<HeaderForm/>
</Box>
</Box>
</>
)
}

View File

@ -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 (
<>
<Box className={classes.jsonHolder}>
<FormControl fullWidth variant="standard">
<Select
displayEmpty
className={classes.select}
labelId="react-json-view-theme"
value={preferedJsonViewerTheme}
label="JSON theme"
onChange={(evt) => {
setPreferedJsonViewerTheme(evt.target.value)
localStorage.setItem(
LocalStorageKeys.PREFERED_JSON_THEME,
evt.target.value,
)
}}
id="json-selector"
>
<MenuItem disabled value="">
<em>JSON theme</em>
</MenuItem>
{ReactJsonViewThemes.map((theme, idx) => (
<MenuItem key={idx} value={theme}>
{theme}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
</>
)
}

View File

@ -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 (
<>
<Box className={classes.localeHolder}>
<FormControl fullWidth variant="standard">
<Select
displayEmpty
className={classes.select}
labelId="prefered-locale"
value={localesList.length > 0 ? preferedLocale : ''}
onChange={(evt) => {
setPreferedLocale(evt.target.value)
}}
id="locale-selector"
>
<MenuItem disabled value="">
<em>Language</em>
</MenuItem>
{localesList.map((locale, idx) => (
<MenuItem key={idx} value={locale}>
{locale}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
</>
)
}

View File

@ -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<ItemWithLocale[]>([])
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 (
<Typography key={item.item._id} variant="body2" className={classes.currentItem}>
{item.locale.Name ? item.locale.Name : item.item._name}
</Typography>
)
} else {
return (
<Link
underline="hover"
color="inherit"
key={idx}
onClick={() => {
setSearchInput(item.item._id)
setSelectedItem(undefined)
}}
className={classes.link}
>
<Typography variant="body2">{item.locale.Name ? item.locale.Name : item.item._name}</Typography>
</Link>
)
}
}
return (
<Box className={classes.breadcrumbHolder}>
<Breadcrumbs aria-label="breadcrumb" className={classes.breadcrumb} id='navigation-breadcrumb'>
<Link
underline="hover"
color="inherit"
key={'home'}
href="/"
id='home-breadcrumb'
className={classes.link}
>
<Typography variant="body2">Home</Typography>
</Link>
{currentHierarchy.map((item, idx) => formatLink(item, idx.toString()))}
</Breadcrumbs>
</Box>
)
}

View File

@ -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<ItemOption[]>([])
const [isbusy, setIsBusy] = useState<boolean>(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 <CircularProgress size={100}/>
// If finished loading
console.log(process.browser)
if (selectedItem && ReactJson !== undefined) {
return (<ReactJson
src={selectedItem!}
theme={preferedJsonViewerTheme}
style={{
marginTop: '2vh',
width: '100%',
overflowY: 'auto',
display: 'flex',
}}
/>)
} else return <Typography id='search-no-data'>No data to display</Typography>
// return <Typography id='search-no-data'>No data to display</Typography>
}
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 (
<Box className={classes.searchAreaHolder}>
<Autocomplete
id='search-autocomplete'
options={selectOptions.map((elt) => ({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) => (
<TextField {...params} label="Search by name or ID"/>
)}
renderOption={(props, option ) => (
<li {...props} key={option.id}><Typography>{option.name}</Typography></li>
)}
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
/>
<Box className={classes.jsonHolder}>{formatDisplayItems()}</Box>
</Box>
)
}

View File

@ -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"
}
}
]}

View File

@ -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
}

View File

@ -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<Prisma.ItemCreateInput, Prisma.ItemUncheckedCreateInput> {
return {
internalId: this._id,
name: this._name,
type: this._type,
props: this._props.toItemPropsData(),
proto: this._proto
}
}
}

View File

@ -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
}
}
}
}
}

View File

@ -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()
}
}
}
}
}

View File

@ -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
}
}
}
}
}

View File

@ -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<Props>) => {
const classes = useStyles();
return (
<>
<Box className={classes.container}>
<Header/>
{props.children}
<Footer/>
</Box>
</>
)
}
export default Layout ;

View File

@ -0,0 +1,122 @@
import { ThemeKeys } from 'react-json-view'
import create from 'zustand'
// import { getLocaleList } from '../dataaccess/ItemBackend'
import { LocalStorageKeys, SessionStorageKeys } from '../dto/SaveKeys'
import { ItemHierarchy } from '../dto/ItemHierarchy'
import { ReactJsonViewThemes } from './ReactJsonViewThemes'
import { ThemeMode } from './ThemeMode'
import { ItemWithLocale } from '../dto/ItemWithLocale';
const getLocaleList = async () => {
return null;
}
export interface GlobalState {
sptarkovWebsiteUrl: string
sptarkovWorkshopUrl: string
sptarkovDocumentationUrl: string
preferedLocale: string
setPreferedLocale: (newLocale: string) => void
localesList: string[]
refreshLocalesList: () => void
preferedJsonViewerTheme: ThemeKeys
setPreferedJsonViewerTheme: (newJsonTheme: string) => void
preferedColorScheme: string
setPreferedColorScheme: (newColorScheme: ThemeMode) => void
searchInput: string
setSearchInput: (newInput: string) => void
desiredSearchInput: string
setDesiredSearchInput: (newInput: string) => void
itemsHierarchy: ItemHierarchy
initHierarchy: () => void
setHierarchy: (newHierarchy: ItemHierarchy) => void
selectedItem: ItemWithLocale | undefined
setSelectedItem: (newSelectedItem: ItemWithLocale | undefined) => void
}
const isClientSide = () => typeof window == 'object' || typeof window !== 'undefined';
const preferedLocale = isClientSide() ? localStorage?.getItem(LocalStorageKeys.PREFERED_LOCALE) : null
const storedPreferedJsonTheme = isClientSide() ? localStorage?.getItem(
LocalStorageKeys.PREFERED_JSON_THEME,
) : ThemeMode.DARK_MODE;
const preferedColorScheme = isClientSide() ? localStorage?.getItem(LocalStorageKeys.PREFERED_COLOR_SCHEME) : null;
export const useGlobalState = create<GlobalState>((set) => ({
sptarkovWebsiteUrl: process.env.REACT_APP_SPTARKOV_HOME ? process.env.REACT_APP_SPTARKOV_HOME : '',
sptarkovWorkshopUrl: process.env.REACT_APP_SPTARKOV_WORKSHOP ? process.env.REACT_APP_SPTARKOV_WORKSHOP : '',
sptarkovDocumentationUrl: process.env.REACT_APP_SPTARKOV_DOCUMENTATION ? process.env.REACT_APP_SPTARKOV_DOCUMENTATION : '',
// Locale
preferedLocale: preferedLocale ? preferedLocale : 'en',
setPreferedLocale: (newLocale: string) => {
if (isClientSide())
localStorage?.setItem(LocalStorageKeys.PREFERED_LOCALE, newLocale)
set((_state) => ({ preferedLocale: newLocale }))
},
localesList: [],
refreshLocalesList: async () => {
const locales = isClientSide() ? sessionStorage?.getItem(SessionStorageKeys.LOCALES) : "[en]";
const localesList = locales !== null && locales !== undefined && locales !== 'undefined' && locales !== 'null' ? JSON.parse(locales) : await getLocaleList()
if (!locales && isClientSide()) sessionStorage?.setItem(SessionStorageKeys.LOCALES, JSON.stringify(localesList ? localesList : null))
set((_state) => ({ localesList: localesList ? localesList : [] }))
},
// Json viewer theme
preferedJsonViewerTheme:
storedPreferedJsonTheme &&
ReactJsonViewThemes.includes(storedPreferedJsonTheme)
? (storedPreferedJsonTheme as ThemeKeys)
: (ReactJsonViewThemes[0] as ThemeKeys),
setPreferedJsonViewerTheme: (newJsonTheme: string) => {
if (isClientSide()) localStorage?.setItem(LocalStorageKeys.PREFERED_JSON_THEME, newJsonTheme)
set((_state) => ({ preferedJsonViewerTheme: newJsonTheme as ThemeKeys }))
},
// Prefered theme
preferedColorScheme: preferedColorScheme
? preferedColorScheme
: ThemeMode.DARK_MODE,
setPreferedColorScheme: (newColorScheme: ThemeMode) => {
if (isClientSide()) localStorage?.setItem(LocalStorageKeys.PREFERED_COLOR_SCHEME, newColorScheme)
set((_state) => ({ preferedColorScheme: newColorScheme }))
},
// SearchInput
searchInput: '',
setSearchInput: (newInput: string) =>
set((_state) => ({ searchInput: newInput })),
desiredSearchInput: '',
setDesiredSearchInput: (newInput: string) =>
set((_state) => ({ desiredSearchInput: newInput })),
// Hierarchy
itemsHierarchy: {},
initHierarchy: () => {
const itemsHierarchy = isClientSide() ? sessionStorage?.getItem(SessionStorageKeys.ITEMS_HIERARCHY) : "{}";
if (itemsHierarchy !== null && itemsHierarchy !== undefined && itemsHierarchy !== 'undefined') {
set((_state) => ({ itemsHierarchy: JSON.parse(itemsHierarchy) }))
}
},
setHierarchy: (newHierarchy: ItemHierarchy) => {
set((state) => {
const newStateHierarchy = Object.assign({}, state.itemsHierarchy, newHierarchy);
if (isClientSide()) sessionStorage?.setItem(SessionStorageKeys.ITEMS_HIERARCHY, JSON.stringify(newStateHierarchy ? newStateHierarchy : null))
return ({
itemsHierarchy: newStateHierarchy,
})
});
},
// Selected item
selectedItem: undefined,
setSelectedItem: (newSelectedItem: ItemWithLocale | undefined) =>
set((_state) => ({ selectedItem: newSelectedItem })),
}))

View File

@ -0,0 +1,21 @@
import {ThemeMode} from '../state/ThemeMode';
import {common, grey, lightBlue, yellow} from '@mui/material/colors';
import { createTheme } from "@mui/material/styles";
export const darkPalette = createTheme({
palette:{
mode: ThemeMode.DARK_MODE,
background: {
default: grey[900],
paper: '#121212'
},
text: {
primary: common.white,
secondary: '#8894a2',
disabled: lightBlue[100]
},
action: {
hover: yellow[700]
},
}
});

View File

@ -0,0 +1,21 @@
import {ThemeMode} from '../state/ThemeMode';
import {blue, common, grey} from "@mui/material/colors";
import {createTheme} from "@mui/material/styles";
export const lightPalette = createTheme({
palette: {
mode: ThemeMode.LIGHT_MODE,
background: {
default: grey[100],
paper: grey[300]
},
text: {
primary: common.black,
secondary: blue[500],
disabled: grey[600]
},
action: {
hover: blue[500],
}
}
});

View File

@ -0,0 +1,12 @@
import { Form, FormProps } from "app/core/components/Form"
import { LabeledTextField } from "app/core/components/LabeledTextField"
import { z } from "zod"
export { FORM_ERROR } from "app/core/components/Form"
export function ItemPrefabForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
return (
<Form<S> {...props}>
<LabeledTextField name="name" label="Name" placeholder="Name" />
</Form>
)
}

View File

@ -0,0 +1,18 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const CreateItemPrefab = z.object({
name: z.string(),
})
export default resolver.pipe(
resolver.zod(CreateItemPrefab),
resolver.authorize(),
async (input) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemPrefab = await db.itemPrefab.create({ data: input })
return itemPrefab
}
)

View File

@ -0,0 +1,18 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const DeleteItemPrefab = z.object({
id: z.number(),
})
export default resolver.pipe(
resolver.zod(DeleteItemPrefab),
resolver.authorize(),
async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemPrefab = await db.itemPrefab.deleteMany({ where: { id } })
return itemPrefab
}
)

View File

@ -0,0 +1,19 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const UpdateItemPrefab = z.object({
id: z.number(),
name: z.string(),
})
export default resolver.pipe(
resolver.zod(UpdateItemPrefab),
resolver.authorize(),
async ({ id, ...data }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemPrefab = await db.itemPrefab.update({ where: { id }, data })
return itemPrefab
}
)

View File

@ -0,0 +1,17 @@
import { resolver, NotFoundError } from "blitz"
import db from "db"
import { z } from "zod"
const GetItemPrefab = z.object({
// This accepts type of undefined, but is required at runtime
id: z.number().optional().refine(Boolean, "Required"),
})
export default resolver.pipe(resolver.zod(GetItemPrefab), resolver.authorize(), async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemPrefab = await db.itemPrefab.findFirst({ where: { id } })
if (!itemPrefab) throw new NotFoundError()
return itemPrefab
})

View File

@ -0,0 +1,30 @@
import { paginate, resolver } from "blitz"
import db, { Prisma } from "db"
interface GetItemPrefabsInput
extends Pick<Prisma.ItemPrefabFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
export default resolver.pipe(
resolver.authorize(),
async ({ where, orderBy, skip = 0, take = 100 }: GetItemPrefabsInput) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const {
items: itemPrefabs,
hasMore,
nextPage,
count,
} = await paginate({
skip,
take,
count: () => db.itemPrefab.count({ where }),
query: (paginateArgs) => db.itemPrefab.findMany({ ...paginateArgs, where, orderBy }),
})
return {
itemPrefabs,
nextPage,
hasMore,
count,
}
}
)

View File

@ -0,0 +1,12 @@
import { Form, FormProps } from "app/core/components/Form"
import { LabeledTextField } from "app/core/components/LabeledTextField"
import { z } from "zod"
export { FORM_ERROR } from "app/core/components/Form"
export function ItemPropForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
return (
<Form<S> {...props}>
<LabeledTextField name="name" label="Name" placeholder="Name" />
</Form>
)
}

View File

@ -0,0 +1,14 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const CreateItemProp = z.object({
name: z.string(),
})
export default resolver.pipe(resolver.zod(CreateItemProp), resolver.authorize(), async (input) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemProp = await db.itemProp.create({ data: input })
return itemProp
})

View File

@ -0,0 +1,14 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const DeleteItemProp = z.object({
id: z.number(),
})
export default resolver.pipe(resolver.zod(DeleteItemProp), resolver.authorize(), async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemProp = await db.itemProp.deleteMany({ where: { id } })
return itemProp
})

View File

@ -0,0 +1,19 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const UpdateItemProp = z.object({
id: z.number(),
name: z.string(),
})
export default resolver.pipe(
resolver.zod(UpdateItemProp),
resolver.authorize(),
async ({ id, ...data }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemProp = await db.itemProp.update({ where: { id }, data })
return itemProp
}
)

View File

@ -0,0 +1,17 @@
import { resolver, NotFoundError } from "blitz"
import db from "db"
import { z } from "zod"
const GetItemProp = z.object({
// This accepts type of undefined, but is required at runtime
id: z.number().optional().refine(Boolean, "Required"),
})
export default resolver.pipe(resolver.zod(GetItemProp), resolver.authorize(), async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemProp = await db.itemProp.findFirst({ where: { id } })
if (!itemProp) throw new NotFoundError()
return itemProp
})

View File

@ -0,0 +1,30 @@
import { paginate, resolver } from "blitz"
import db, { Prisma } from "db"
interface GetItemPropsInput
extends Pick<Prisma.ItemPropFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
export default resolver.pipe(
resolver.authorize(),
async ({ where, orderBy, skip = 0, take = 100 }: GetItemPropsInput) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const {
items: itemProps,
hasMore,
nextPage,
count,
} = await paginate({
skip,
take,
count: () => db.itemProp.count({ where }),
query: (paginateArgs) => db.itemProp.findMany({ ...paginateArgs, where, orderBy }),
})
return {
itemProps,
nextPage,
hasMore,
count,
}
}
)

View File

@ -0,0 +1,12 @@
import { Form, FormProps } from "app/core/components/Form"
import { LabeledTextField } from "app/core/components/LabeledTextField"
import { z } from "zod"
export { FORM_ERROR } from "app/core/components/Form"
export function ItemUsePrefabForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
return (
<Form<S> {...props}>
<LabeledTextField name="name" label="Name" placeholder="Name" />
</Form>
)
}

View File

@ -0,0 +1,18 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const CreateItemUsePrefab = z.object({
name: z.string(),
})
export default resolver.pipe(
resolver.zod(CreateItemUsePrefab),
resolver.authorize(),
async (input) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemUsePrefab = await db.itemUsePrefab.create({ data: input })
return itemUsePrefab
}
)

View File

@ -0,0 +1,18 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const DeleteItemUsePrefab = z.object({
id: z.number(),
})
export default resolver.pipe(
resolver.zod(DeleteItemUsePrefab),
resolver.authorize(),
async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemUsePrefab = await db.itemUsePrefab.deleteMany({ where: { id } })
return itemUsePrefab
}
)

View File

@ -0,0 +1,19 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const UpdateItemUsePrefab = z.object({
id: z.number(),
name: z.string(),
})
export default resolver.pipe(
resolver.zod(UpdateItemUsePrefab),
resolver.authorize(),
async ({ id, ...data }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemUsePrefab = await db.itemUsePrefab.update({ where: { id }, data })
return itemUsePrefab
}
)

View File

@ -0,0 +1,21 @@
import { resolver, NotFoundError } from "blitz"
import db from "db"
import { z } from "zod"
const GetItemUsePrefab = z.object({
// This accepts type of undefined, but is required at runtime
id: z.number().optional().refine(Boolean, "Required"),
})
export default resolver.pipe(
resolver.zod(GetItemUsePrefab),
resolver.authorize(),
async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const itemUsePrefab = await db.itemUsePrefab.findFirst({ where: { id } })
if (!itemUsePrefab) throw new NotFoundError()
return itemUsePrefab
}
)

View File

@ -0,0 +1,30 @@
import { paginate, resolver } from "blitz"
import db, { Prisma } from "db"
interface GetItemUsePrefabsInput
extends Pick<Prisma.ItemUsePrefabFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
export default resolver.pipe(
resolver.authorize(),
async ({ where, orderBy, skip = 0, take = 100 }: GetItemUsePrefabsInput) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const {
items: itemUsePrefabs,
hasMore,
nextPage,
count,
} = await paginate({
skip,
take,
count: () => db.itemUsePrefab.count({ where }),
query: (paginateArgs) => db.itemUsePrefab.findMany({ ...paginateArgs, where, orderBy }),
})
return {
itemUsePrefabs,
nextPage,
hasMore,
count,
}
}
)

View File

@ -0,0 +1,12 @@
import { Form, FormProps } from "app/core/components/Form"
import { LabeledTextField } from "app/core/components/LabeledTextField"
import { z } from "zod"
export { FORM_ERROR } from "app/core/components/Form"
export function ItemForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
return (
<Form<S> {...props}>
<LabeledTextField name="name" label="Name" placeholder="Name" />
</Form>
)
}

View File

@ -0,0 +1,16 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const CreateItem = z.object({
id: z.number().optional().refine(Boolean, "Required"),
name: z.string().optional().refine(String, "Required"),
type: z.string().optional().refine(String, "Required"),
})
export default resolver.pipe(resolver.zod(CreateItem), resolver.authorize(), async (input) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const item = await db.item.create({ data: input })
return item
})

View File

@ -0,0 +1,14 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const DeleteItem = z.object({
id: z.number(),
})
export default resolver.pipe(resolver.zod(DeleteItem), resolver.authorize(), async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const item = await db.item.deleteMany({ where: { id } })
return item
})

View File

@ -0,0 +1,19 @@
import { resolver } from "blitz"
import db from "db"
import { z } from "zod"
const UpdateItem = z.object({
id: z.number(),
name: z.string(),
})
export default resolver.pipe(
resolver.zod(UpdateItem),
resolver.authorize(),
async ({ id, ...data }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const item = await db.item.update({ where: { id }, data })
return item
}
)

View File

@ -0,0 +1,17 @@
import { resolver, NotFoundError } from "blitz"
import db from "db"
import { z } from "zod"
const GetItem = z.object({
// This accepts type of undefined, but is required at runtime
id: z.number().optional().refine(Boolean, "Required")
})
export default resolver.pipe(resolver.zod(GetItem), resolver.authorize(), async ({ id }) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const item = await db.item.findFirst({ where: { id } })
if (!item) throw new NotFoundError()
return item
})

View File

@ -0,0 +1,30 @@
import { paginate, resolver } from "blitz"
import db, { Prisma } from "db"
interface GetItemsInput
extends Pick<Prisma.ItemFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
export default resolver.pipe(
resolver.authorize(),
async ({ where, orderBy, skip = 0, take = 100 }: GetItemsInput) => {
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
const {
items: items,
hasMore,
nextPage,
count,
} = await paginate({
skip,
take,
count: () => db.item.count({ where }),
query: (paginateArgs) => db.item.findMany({ ...paginateArgs, where, orderBy }),
})
return {
items,
nextPage,
hasMore,
count,
}
}
)

View File

@ -0,0 +1,8 @@
import greetingsQueue from "app/api/greetingsQueue"
export default async function enqueueGreeting() {
await greetingsQueue.enqueue({
to: "Sandy Cheeks",
message: "Howdy!",
})
}

49
db/app/pages/404.tsx Normal file
View File

@ -0,0 +1,49 @@
import {Box, Theme, Typography} from '@mui/material'
import {makeStyles} from "@mui/styles";
import { Layout } from 'app/core/layouts/Layout';
import { BlitzPage } from 'blitz';
import React from "react";
const useStyles = makeStyles((theme: Theme) => ({
searchContainer: {
display: 'flex',
flexDirection: 'row',
flexGrow: 1,
padding: '2vh 2vw 1vh 2vw'
},
notFoundAreaHolder: {
display: 'flex',
flexGrow: 1,
flexDirection: 'column',
background: theme.palette.background.paper,
padding: '2vh 2vw 2vh 2vw',
},
notFoundContainer: {
display: 'flex',
flexGrow: 1,
flexDirection: 'column',
width: "100%",
alignItems: "center",
paddingTop: "10vh"
},
}))
const PageNotFound: BlitzPage = () => {
const classes = useStyles();
return (
<>
<Box className={classes.searchContainer}>
<Box className={classes.notFoundAreaHolder}>
<Box className={classes.notFoundContainer}>
<Typography id={'not-found-message'} variant={"h3"}>This page does not exist !</Typography>
</Box>
</Box>
</Box>
</>
)
}
PageNotFound.getLayout = (page) => <Layout>{page}</Layout>
PageNotFound.suppressFirstRenderFlicker = true
export default PageNotFound;

63
db/app/pages/_app.tsx Normal file
View File

@ -0,0 +1,63 @@
import {
AppProps,
ErrorBoundary,
ErrorComponent,
ErrorFallbackProps,
useQueryErrorResetBoundary,
} from "blitz"
import CssBaseline from "@mui/material/CssBaseline";
import { ThemeProvider } from "@mui/material/styles";
import React, { useEffect, useMemo, useState } from "react";
import { useGlobalState } from "app/core/state/GlobalState";
import { LocalStorageKeys } from "app/core/dto/SaveKeys";
import { ThemeMode } from "app/core/state/ThemeMode";
import { darkPalette } from "app/core/theme/darkTheme";
import { lightPalette } from "app/core/theme/lightTheme";
import { getTheme } from "app/core/theme/Theme";
import { useTheme } from "next-themes";
const App = ({ Component, pageProps }: AppProps) => {
const [mounted, setMounted] = useState(false)
const {theme: prefersDarkMode} = useTheme()
const [preferedColorScheme ,setPreferedColorScheme] = useGlobalState(state => [state.preferedColorScheme, state.setPreferedColorScheme])
const reset = useQueryErrorResetBoundary().reset
useEffect(() => {
const localPreferedTheme = localStorage.getItem(LocalStorageKeys.PREFERED_COLOR_SCHEME);
if (localPreferedTheme) {
setPreferedColorScheme(localPreferedTheme as ThemeMode)
return
}
const preferedTheme = prefersDarkMode ? ThemeMode.DARK_MODE : ThemeMode.LIGHT_MODE;
setPreferedColorScheme(preferedTheme)
// eslint-disable-next-line
}, [prefersDarkMode]) // Need to be only used on prefersDarkMode change
const getLayout = Component.getLayout || ((page) => page)
const palette = useMemo(
()=> getTheme(preferedColorScheme === ThemeMode.DARK_MODE ? darkPalette.palette : lightPalette.palette),
[preferedColorScheme]);
useEffect(() => setMounted(true), [])
if (!mounted) return null
return (
<ThemeProvider theme={palette}>
<CssBaseline />
<ErrorBoundary
FallbackComponent={RootErrorFallback}
onReset={reset}
>
{getLayout(<Component {...pageProps} />)}
</ErrorBoundary>
</ThemeProvider>
);
}
function RootErrorFallback({ error }: ErrorFallbackProps) {
return <ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} />
}
export default App;

View File

@ -0,0 +1,50 @@
import {
DocumentContext,
Document,
Html,
DocumentHead,
Main,
BlitzScript /*DocumentContext*/,
} from "blitz";
import React from "react";
import { ServerStyleSheets } from "@mui/styles";
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () => originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />)
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()]
};
}
// Only uncomment if you need to customize this behaviour
// static async getInitialProps(ctx: DocumentContext) {
// const initialProps = await Document.getInitialProps(ctx)
// return {...initialProps}
// }
render() {
return (
<Html lang="en">
<DocumentHead />
<title>Item Finder</title>
<body>
<Main />
<BlitzScript />
</body>
</Html>
)
}
}
export default MyDocument

View File

@ -0,0 +1,13 @@
import { render } from "test/utils"
import Home from "./index"
test.skip("renders blitz documentation link", () => {
// This is an example of how to ensure a specific item is in the document
// But it's disabled by default (by test.skip) so the test doesn't fail
// when you remove the the default content from the page
const { getByText } = render(<Home />)
const linkElement = getByText(/Documentation/i)
expect(linkElement).toBeInTheDocument()
})

44
db/app/pages/index.tsx Normal file
View File

@ -0,0 +1,44 @@
import {Box, Theme, Typography} from '@mui/material'
import {makeStyles} from "@mui/styles";
import React from "react";
import { BlitzPage } from 'blitz';
import { Layout } from 'app/core/layouts/Layout';
import { NavigationBreadcrumb } from 'app/core/components/NavigationBreadcrumb';
import { SearchArea } from 'app/core/components/SearchArea';
const useStyles = makeStyles((theme: Theme) => ({
container: {
background: theme.palette.background.default,
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
height: '100vh',
maxheight: '100vh',
},
searchContainer: {
display: 'flex',
flexDirection: 'row',
flexGrow: 1,
padding: '2vh 2vw 1vh 2vw'
}
}));
const Search: BlitzPage = () => {
const classes = useStyles();
return (
<>
<Box className={classes.container}>
<NavigationBreadcrumb/>
<Box className={classes.searchContainer}>
<SearchArea/>
</Box>
</Box>
</>
)
}
Search.getLayout = (page) => <Layout>{page}</Layout>
Search.suppressFirstRenderFlicker = true
export default Search;

4
db/babel.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
presets: ["blitz/babel"],
plugins: [],
}

6
db/blitz-env.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

13
db/blitz.config.ts Normal file
View File

@ -0,0 +1,13 @@
import { BlitzConfig } from "blitz"
const config: BlitzConfig = {
/* Uncomment this to customize the webpack config
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
// Note: we provide webpack above so you should not `require` it
// Perform customizations to webpack config
// Important: return the modified config
return config
},
*/
}
module.exports = config

7
db/db/index.ts Normal file
View File

@ -0,0 +1,7 @@
import { enhancePrisma } from "blitz"
import { PrismaClient } from "@prisma/client"
const EnhancedPrisma = enhancePrisma(PrismaClient)
export * from "@prisma/client"
export default new EnhancedPrisma()

View File

@ -0,0 +1,41 @@
-- CreateTable
CREATE TABLE "Item" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"name" TEXT NOT NULL,
"type" TEXT NOT NULL,
"itemId" INTEGER NOT NULL,
CONSTRAINT "Item_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ItemProp" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"name" TEXT NOT NULL,
"shortName" TEXT NOT NULL,
"description" TEXT NOT NULL,
"weight" INTEGER NOT NULL,
"backgroundcolor" TEXT NOT NULL,
"width" INTEGER NOT NULL,
"height" INTEGER NOT NULL,
"stackMaxSize" INTEGER NOT NULL,
"itemSound" TEXT NOT NULL,
"stackObjectsCount" INTEGER NOT NULL,
"notShownInSlot" BOOLEAN NOT NULL,
"examinedByDefault" BOOLEAN NOT NULL,
"examineTime" INTEGER NOT NULL,
"isUndiscardable" BOOLEAN NOT NULL,
"isUnsaleable" BOOLEAN NOT NULL,
"isUnbuyable" BOOLEAN NOT NULL,
"isUngivable" BOOLEAN NOT NULL,
"isLockedAfterEquip" BOOLEAN NOT NULL,
CONSTRAINT "ItemProp_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Item" ADD CONSTRAINT "Item_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;

View File

@ -0,0 +1,10 @@
-- CreateTable
CREATE TABLE "ItemPrefab" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"path" TEXT NOT NULL,
"rcid" TEXT NOT NULL,
CONSTRAINT "ItemPrefab_pkey" PRIMARY KEY ("id")
);

View File

@ -0,0 +1,10 @@
-- CreateTable
CREATE TABLE "ItemUsePrefab" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"path" TEXT NOT NULL,
"rcid" TEXT NOT NULL,
CONSTRAINT "ItemUsePrefab_pkey" PRIMARY KEY ("id")
);

View File

@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "ItemProp" ADD COLUMN "conflictingItems" TEXT[],
ADD COLUMN "unlootableFromSide" TEXT[];

View File

@ -0,0 +1,18 @@
/*
Warnings:
- Added the required column `itemPrefabId` to the `ItemProp` table without a default value. This is not possible if the table is not empty.
- Added the required column `itemProp` to the `ItemProp` table without a default value. This is not possible if the table is not empty.
- Added the required column `itemUsePrefabId` to the `ItemProp` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "ItemProp" ADD COLUMN "itemPrefabId" INTEGER NOT NULL,
ADD COLUMN "itemProp" TEXT NOT NULL,
ADD COLUMN "itemUsePrefabId" INTEGER NOT NULL;
-- AddForeignKey
ALTER TABLE "ItemProp" ADD CONSTRAINT "ItemProp_itemPrefabId_fkey" FOREIGN KEY ("itemPrefabId") REFERENCES "ItemPrefab"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ItemProp" ADD CONSTRAINT "ItemProp_itemUsePrefabId_fkey" FOREIGN KEY ("itemUsePrefabId") REFERENCES "ItemUsePrefab"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,11 @@
/*
Warnings:
- Added the required column `itemPropId` to the `Item` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Item" ADD COLUMN "itemPropId" INTEGER NOT NULL;
-- AddForeignKey
ALTER TABLE "Item" ADD CONSTRAINT "Item_itemPropId_fkey" FOREIGN KEY ("itemPropId") REFERENCES "ItemProp"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,8 @@
/*
Warnings:
- Added the required column `internalId` to the `Item` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Item" ADD COLUMN "internalId" TEXT NOT NULL;

View File

@ -0,0 +1,8 @@
/*
Warnings:
- A unique constraint covering the columns `[internalId]` on the table `Item` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateIndex
CREATE UNIQUE INDEX "Item_internalId_key" ON "Item"("internalId");

View File

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

79
db/db/schema.prisma Normal file
View File

@ -0,0 +1,79 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model Item {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String
type String
parentItem Item? @relation("ItemToItem", fields: [parent], references: [internalId], onDelete: NoAction, onUpdate: NoAction)
Item Item[] @relation("ItemToItem")
parent String
internalId String @unique
props ItemProp @relation(fields: [itemPropId], references: [id])
itemPropId Int
proto String?
}
model ItemProp {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Name String
ShortName String
Description String
Weight Int
Backgroundcolor String
Width Int
Height Int
StackMaxSize Int
ItemSound String
StackObjectsCount Int
NotShownInSlot Boolean
ExaminedByDefault Boolean
ExamineTime Int
IsUndiscardable Boolean
IsUnsaleable Boolean
IsUnbuyable Boolean
IsUngivable Boolean
IsLockedAfterEquip Boolean
ConflictingItems String[]
UnlootableFromSide String[]
Prefab ItemPrefab @relation(fields: [ItemPrefabId], references: [id])
UsePrefab ItemUsePrefab @relation(fields: [ItemUsePrefabId], references: [id])
ItemPrefabId Int
ItemUsePrefabId Int
Item Item[]
}
model ItemPrefab {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
path String
rcid String
ItemProp ItemProp[]
}
model ItemUsePrefab {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
path String
rcid String
ItemProp ItemProp[]
}

15
db/db/seeds.ts Normal file
View File

@ -0,0 +1,15 @@
// import db from "./index"
/*
* This seed function is executed when you run `blitz db seed`.
*
* Probably you want to use a library like https://chancejs.com
* to easily generate realistic data.
*/
const seed = async () => {
// for (let i = 0; i < 5; i++) {
// await db.project.create({ data: { name: "Project " + i } })
// }
}
export default seed

7
db/jest.config.ts Normal file
View File

@ -0,0 +1,7 @@
import type { Config } from "@jest/types"
const config: Config.InitialOptions = {
preset: "blitz",
}
export default config

58
db/package.json Normal file
View File

@ -0,0 +1,58 @@
{
"name": "db",
"version": "1.0.0",
"type": "commonjs",
"scripts": {
"dev": "concurrently --raw \"blitz dev\" 'quirrel'",
"build": "blitz build",
"start": "blitz start",
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
"test": "jest",
"test:watch": "jest --watch",
"studio": "blitz prisma studio",
"blitz": "blitz"
},
"prettier": {
"semi": false,
"printWidth": 100
},
"lint-staged": {
"*.{js,ts,tsx}": [
"eslint --fix"
]
},
"prisma": {
"schema": "db/schema.prisma"
},
"engines": {
"node": ">=12.2.0"
},
"dependencies": {
"@emotion/react": "11.x",
"@emotion/styled": "11.x",
"@mui/icons-material": "5.2.4",
"@mui/material": "5.x",
"@mui/styles": "5.x",
"@prisma/client": "3.12.0",
"blitz": "0.44.4",
"final-form": "4.20.6",
"next-themes": "0.0.15",
"quirrel": "1.x",
"react": "18.0.0-beta-149b420f6-20211119",
"react-dom": "18.0.0-alpha-5ca4b0433-20211020",
"react-final-form": "6.5.9",
"react-json-view": "1.21.3",
"zustand": "3.6.7"
},
"devDependencies": {
"@types/react": "17.0.37",
"concurrently": "6.x",
"eslint": "7.32.0",
"lint-staged": "11.3.0-beta.2",
"prettier": "2.5.1",
"pretty-quick": "3.1.2",
"prisma": "3.12.0",
"typescript": "~4.5"
},
"private": true
}

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
db/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

4
db/test/setup.ts Normal file
View File

@ -0,0 +1,4 @@
// This is the jest 'setupFilesAfterEnv' setup file
// It's a good place to set globals, add global before/after hooks, etc
export {} // so TS doesn't complain

105
db/test/utils.tsx Normal file
View File

@ -0,0 +1,105 @@
import { RouterContext, BlitzRouter, BlitzProvider } from "blitz"
import { render as defaultRender } from "@testing-library/react"
import { renderHook as defaultRenderHook } from "@testing-library/react-hooks"
export * from "@testing-library/react"
// --------------------------------------------------------------------------------
// This file customizes the render() and renderHook() test functions provided
// by React testing library. It adds a router context wrapper with a mocked router.
//
// You should always import `render` and `renderHook` from this file
//
// This is the place to add any other context providers you need while testing.
// --------------------------------------------------------------------------------
// --------------------------------------------------
// render()
// --------------------------------------------------
// Override the default test render with our own
//
// You can override the router mock like this:
//
// const { baseElement } = render(<MyComponent />, {
// router: { pathname: '/my-custom-pathname' },
// });
// --------------------------------------------------
export function render(
ui: RenderUI,
{ wrapper, router, dehydratedState, ...options }: RenderOptions = {}
) {
if (!wrapper) {
// Add a default context wrapper if one isn't supplied from the test
wrapper = ({ children }) => (
<BlitzProvider dehydratedState={dehydratedState}>
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
{children}
</RouterContext.Provider>
</BlitzProvider>
)
}
return defaultRender(ui, { wrapper, ...options })
}
// --------------------------------------------------
// renderHook()
// --------------------------------------------------
// Override the default test renderHook with our own
//
// You can override the router mock like this:
//
// const result = renderHook(() => myHook(), {
// router: { pathname: '/my-custom-pathname' },
// });
// --------------------------------------------------
export function renderHook(
hook: RenderHook,
{ wrapper, router, dehydratedState, ...options }: RenderHookOptions = {}
) {
if (!wrapper) {
// Add a default context wrapper if one isn't supplied from the test
wrapper = ({ children }) => (
<BlitzProvider dehydratedState={dehydratedState}>
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
{children}
</RouterContext.Provider>
</BlitzProvider>
)
}
return defaultRenderHook(hook, { wrapper, ...options })
}
export const mockRouter: BlitzRouter = {
basePath: "",
pathname: "/",
route: "/",
asPath: "/",
params: {},
query: {},
isReady: true,
isLocaleDomain: false,
isPreview: false,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
back: jest.fn(),
prefetch: jest.fn(),
beforePopState: jest.fn(),
events: {
on: jest.fn(),
off: jest.fn(),
emit: jest.fn(),
},
isFallback: false,
}
type DefaultParams = Parameters<typeof defaultRender>
type RenderUI = DefaultParams[0]
type RenderOptions = DefaultParams[1] & { router?: Partial<BlitzRouter>; dehydratedState?: unknown }
type DefaultHookParams = Parameters<typeof defaultRenderHook>
type RenderHook = DefaultHookParams[0]
type RenderHookOptions = DefaultHookParams[1] & {
router?: Partial<BlitzRouter>
dehydratedState?: unknown
}

24
db/tsconfig.json Normal file
View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"baseUrl": "./",
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo",
},
"exclude": ["node_modules", "**/*.e2e.ts", "cypress"],
"include": ["blitz-env.d.ts", "**/*.ts", "**/*.tsx"]
}

5
db/types.ts Normal file
View File

@ -0,0 +1,5 @@
import { DefaultCtx } from "blitz"
declare module "blitz" {
export interface Ctx extends DefaultCtx {}
}

11431
db/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More