organized the dev tools dahsboard componetns and connected them tothe backend slightly. Still working on how to display combos as a tree, then the inspector should follow.
This commit is contained in:
38
src/app/api/dev/vor/cards/[data_id]/route.ts
Normal file
38
src/app/api/dev/vor/cards/[data_id]/route.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { Pool } from "pg";
|
||||||
|
|
||||||
|
const db = new Pool({
|
||||||
|
connectionString: process.env.DB_STRING!
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: Promise<{ data_id: string }> }
|
||||||
|
){
|
||||||
|
try{
|
||||||
|
const { data_id } = await params;
|
||||||
|
if(!data_id){
|
||||||
|
return NextResponse.json({ message: "Invalid search parameter:" + data_id }, { status: 400, statusText: "Malformed request" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const combosRes = await db.query(
|
||||||
|
`WITH RECURSIVE t AS (
|
||||||
|
SELECT parent_data_id, combo_data_id, result_data_id, 1 AS depth
|
||||||
|
FROM combos
|
||||||
|
WHERE parent_data_id = $1
|
||||||
|
UNION ALL
|
||||||
|
SELECT c.parent_data_id, c.combo_data_id, c.result_data_id, t.depth + 1
|
||||||
|
FROM combos c
|
||||||
|
JOIN t on c.parent_data_id = t.result_data_id
|
||||||
|
WHERE t.depth < 3
|
||||||
|
)
|
||||||
|
SELECT * FROM t ORDER BY depth, parent_data_id, combo_data_id;`, [data_id]
|
||||||
|
);
|
||||||
|
return NextResponse.json(combosRes.rows ?? [])
|
||||||
|
|
||||||
|
}catch(error){
|
||||||
|
return NextResponse.json({ message: "Could not GET combos: " + error }, { status: 500, statusText: "Serverside exception" })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
18
src/app/api/dev/vor/cards/route.ts
Normal file
18
src/app/api/dev/vor/cards/route.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { NextResponse } from "next/server"
|
||||||
|
import { Pool } from "pg"
|
||||||
|
|
||||||
|
const db = new Pool({
|
||||||
|
connectionString: process.env.DB_STRING!
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function GET(){
|
||||||
|
try{
|
||||||
|
const result = await db.query("SELECT * FROM cards;")
|
||||||
|
console.log("Got some combos")
|
||||||
|
return NextResponse.json(result.rows ?? [])
|
||||||
|
}catch(err){
|
||||||
|
console.error("Could not GET cards:", err)
|
||||||
|
return NextResponse.json( { message: "Serverside exception occurred " + err }, { status: 500 } )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -19,8 +19,7 @@ export async function GET(req: NextRequest){
|
|||||||
|
|
||||||
export async function POST(req: NextRequest){
|
export async function POST(req: NextRequest){
|
||||||
try{
|
try{
|
||||||
const body = (await req.json()) as { name: string, expiration_date: string }
|
const { name, expiration_date } = (await req.json()) as { name: string, expiration_date: string }
|
||||||
console.log("GOT BODY:", body)
|
|
||||||
const result = await db.query("INSERT INTO products (name, exp_date) VALUES ($1, $2) RETURNING record_num;", [name, expiration_date]);
|
const result = await db.query("INSERT INTO products (name, exp_date) VALUES ($1, $2) RETURNING record_num;", [name, expiration_date]);
|
||||||
return NextResponse.json(result.rows[0].record_num ?? []);
|
return NextResponse.json(result.rows[0].record_num ?? []);
|
||||||
} catch(error){
|
} catch(error){
|
||||||
|
|||||||
@@ -1,67 +1,20 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { SyntheticEvent } from "react";
|
import React from "react";
|
||||||
import styles from "@/styles/dev-tools.module.css"
|
import styles from "@/styles/dev-tools.module.css"
|
||||||
import { Container, Select, Stack, Chip, MenuItem, InputLabel, Snackbar, IconButton, SnackbarCloseReason, Typography } from "@mui/material"
|
import { IconButton, Snackbar, SnackbarCloseReason, Typography } from "@mui/material"
|
||||||
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
|
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import Draggable from 'react-draggable';
|
import { Card, Combo, EditorMode, Unit } from "@/types/dev-tools";
|
||||||
|
import { EditorContext } from "@/contexts/EditorContext";
|
||||||
type EditorMode = "insert" | "update";
|
import LeftMenu from "@/components/dev-tools/LeftMenu";
|
||||||
|
import { useEditor } from "@/hooks/useEditor";
|
||||||
interface DBObject {
|
import Editor from "@/components/dev-tools/Editor";
|
||||||
record_num: number
|
|
||||||
data_id: string;
|
|
||||||
change_timestamp: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Unit extends DBObject {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Card extends DBObject {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EditorContextType {
|
|
||||||
editorMode: "insert" | "update";
|
|
||||||
currentRecordNum: number | null;
|
|
||||||
currentDataId: string | null;
|
|
||||||
cards: Card[] | null;
|
|
||||||
units: Unit[] | null;
|
|
||||||
error: string | null,
|
|
||||||
|
|
||||||
changeEditorMode?: (m: string) => void;
|
|
||||||
changeCurrentRecordNum?: (n: number) => void;
|
|
||||||
changeCurrentDataId?: (n: string) => void;
|
|
||||||
changeError?: (e: string) => void;
|
|
||||||
}
|
|
||||||
const EditorContext = React.createContext<EditorContextType>({
|
|
||||||
currentRecordNum: null,
|
|
||||||
currentDataId: null,
|
|
||||||
editorMode: "insert",
|
|
||||||
cards: null,
|
|
||||||
units: null,
|
|
||||||
error: null,
|
|
||||||
})
|
|
||||||
|
|
||||||
function useEditor() {
|
|
||||||
const ctx = React.useContext(EditorContext);
|
|
||||||
if(!ctx){
|
|
||||||
throw new Error("Error: use Editor must be used within an EditorContext")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const unitTypes: string[] = ["minion", "enemy", "hero", "ally"];
|
|
||||||
const buildingTypes: string[] = ["enemy", "minion", "terrain", "support"];
|
|
||||||
|
|
||||||
export default function Page(){
|
export default function Page(){
|
||||||
const [currentRecordNum, setCurrentRecordNum] = React.useState<number>(0);
|
const [currentRecordNum, setCurrentRecordNum] = React.useState<number>(0);
|
||||||
const [currentDataId, setCurrentDataId] = React.useState<string>("");
|
const [currentDataId, setCurrentDataId] = React.useState<string>("");
|
||||||
const [editorMode, setEditorMode] = React.useState<EditorMode>("insert");
|
const [editorMode, setEditorMode] = React.useState<EditorMode>("insert");
|
||||||
const [cards, setCards] = React.useState<Card[] | null>(null);
|
const [cards, setCards] = React.useState<Card[] | null>(null);
|
||||||
|
const [currentCombos, setCurrentCombos] = React.useState<Combo[] | null>(null);
|
||||||
const [units, setUnits] = React.useState<Unit[] | null>(null);
|
const [units, setUnits] = React.useState<Unit[] | null>(null);
|
||||||
const [error, setError] = React.useState<string | null>(null);
|
const [error, setError] = React.useState<string | null>(null);
|
||||||
const [open, setOpen] = React.useState<boolean>(false);
|
const [open, setOpen] = React.useState<boolean>(false);
|
||||||
@@ -73,27 +26,70 @@ export default function Page(){
|
|||||||
setEditorMode(m)
|
setEditorMode(m)
|
||||||
}
|
}
|
||||||
const changeError = (e: string) => setError(e);
|
const changeError = (e: string) => setError(e);
|
||||||
// get all of the cards, or units
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const getCards = async () => {
|
const getCards = async () => {
|
||||||
try{
|
try{
|
||||||
// fetch our db
|
// fetch our db
|
||||||
|
const res = await fetch("/api/dev/vor/cards", {
|
||||||
|
method: "GET",
|
||||||
|
})
|
||||||
|
const body = await res.json()
|
||||||
|
if(!res.ok){
|
||||||
|
console.error("Could not get vor cards:", body)
|
||||||
|
setCards(null);
|
||||||
|
} else{
|
||||||
|
setCards(body)
|
||||||
|
}
|
||||||
}catch (error) {
|
}catch (error) {
|
||||||
|
console.error("Could not get vor cards:", error)
|
||||||
|
setCards(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUnits = async () => {
|
const getUnits = async () => {
|
||||||
try{
|
try{
|
||||||
|
// fetch our db
|
||||||
|
const res = await fetch("/api/dev/vor/units", {
|
||||||
|
method: "GET",
|
||||||
|
})
|
||||||
|
const body = await res.json()
|
||||||
|
if(!res.ok){
|
||||||
|
console.error("Could not get vor units:", body)
|
||||||
|
setUnits(null);
|
||||||
|
} else{
|
||||||
|
setUnits(body)
|
||||||
|
}
|
||||||
}catch (error) {
|
}catch (error) {
|
||||||
|
console.error("Could not get vor units:", error)
|
||||||
|
setUnits(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getCards();
|
getCards();
|
||||||
getUnits();
|
getUnits();
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const getCombos = async () => {
|
||||||
|
// first check if the user has unconfirmed changes, if not then go for the query
|
||||||
|
try{
|
||||||
|
const res = await fetch(`/api/dev/vor/${currentDataId}/combos`, {
|
||||||
|
method: "GET",
|
||||||
|
})
|
||||||
|
const body = await res.json();
|
||||||
|
if(res.ok){
|
||||||
|
setCurrentCombos(body)
|
||||||
|
}else{
|
||||||
|
setCurrentCombos(null)
|
||||||
|
console.error("Failed to get combos for:", body)
|
||||||
|
}
|
||||||
|
} catch(error){
|
||||||
|
setCurrentCombos(null)
|
||||||
|
console.error("Could not get combos for:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},[currentDataId])
|
||||||
|
|
||||||
const handleClose = (
|
const handleClose = (
|
||||||
event: React.SyntheticEvent | Event,
|
event: React.SyntheticEvent | Event,
|
||||||
reason?: SnackbarCloseReason,
|
reason?: SnackbarCloseReason,
|
||||||
@@ -130,6 +126,7 @@ export default function Page(){
|
|||||||
error,
|
error,
|
||||||
cards,
|
cards,
|
||||||
units,
|
units,
|
||||||
|
currentCombos,
|
||||||
|
|
||||||
changeCurrentRecordNum,
|
changeCurrentRecordNum,
|
||||||
changeCurrentDataId,
|
changeCurrentDataId,
|
||||||
@@ -154,208 +151,3 @@ export default function Page(){
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DynamicCard() {
|
|
||||||
// to add a new kind of data type "map", "string", "bool", etc. they add a prop for it.
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface ThemeSelection {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const debugCard = [
|
|
||||||
{
|
|
||||||
id: "terrain_forest",
|
|
||||||
label: "Forest",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: "terrain_haunted_forest",
|
|
||||||
label: "Haunted Forest",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'terrain_undead_candy_forest',
|
|
||||||
label: "Undead Candy Forest"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "support_blacksmith",
|
|
||||||
label: "Forest",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: "support_terrain_haunted_forest",
|
|
||||||
label: "Haunted Forest",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'SUppror_terrain_undead_candy_forest',
|
|
||||||
label: "Undead Candy Forest"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "enemy_graveyard",
|
|
||||||
label: "Forest",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: "enemy_terrain_haunted_forest",
|
|
||||||
label: "Haunted Forest",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'enemy_terrain_undead_candy_forest',
|
|
||||||
label: "Undead Candy Forest"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
function LeftMenu(){
|
|
||||||
|
|
||||||
const [themes, setThemes] = React.useState<ThemeSelection[]>([]); // get from the database
|
|
||||||
const [menuMode, setMenuMode] = React.useState<"unit" | "card">("card");
|
|
||||||
|
|
||||||
const { currentRecordNum, } = useEditor();
|
|
||||||
|
|
||||||
const handleSelectAsset = (e: React.MouseEvent<Element, MouseEvent>) => {
|
|
||||||
console.log("Got e:", e)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Container className={styles.leftmenu}>
|
|
||||||
<Stack >
|
|
||||||
<Typography id="assetTypeFilter">Asset Type</Typography>
|
|
||||||
<Select className="assetTypeFilter">
|
|
||||||
<MenuItem value="">All</MenuItem>
|
|
||||||
{menuMode === "card" ?
|
|
||||||
buildingTypes.map((opt, i) => (<MenuItem value={opt} key={i}>{opt[0].toUpperCase() + opt.slice(1)}</MenuItem>)) :
|
|
||||||
unitTypes.map((opt, i) => (<MenuItem value={opt} key={i}>{opt[0].toUpperCase() + opt.slice(1)}</MenuItem>))
|
|
||||||
}
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<InputLabel id="themeFilter">Theme</InputLabel>
|
|
||||||
<Select className="themeFilter" label="Theme" >
|
|
||||||
<MenuItem value="">All</MenuItem>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
</Stack>
|
|
||||||
<Typography >
|
|
||||||
Current {menuMode}s created
|
|
||||||
</Typography>
|
|
||||||
<RichTreeView items={debugCard} onItemClick={handleSelectAsset}>
|
|
||||||
</RichTreeView>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function Editor(){
|
|
||||||
const { editorMode } = useEditor();
|
|
||||||
|
|
||||||
const canvasWrapperRef = React.useRef<HTMLDivElement | null>(null);
|
|
||||||
const canvasRef = React.useRef<HTMLCanvasElement | null>(null);
|
|
||||||
|
|
||||||
const onMouseUp = (e: React.MouseEvent) => {
|
|
||||||
|
|
||||||
console.log("event for mouse up", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onMouseDown = (e: React.MouseEvent) => {
|
|
||||||
|
|
||||||
console.log("event for mouse down", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDragOver = (e: React.DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
//console.log("event for drag over:", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDrop = (e: React.DragEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const raw = e.dataTransfer.getData("text/plain");
|
|
||||||
console.log("DROP canvas raw:", raw);
|
|
||||||
// create a box?
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container className={styles.editor} maxWidth={false} disableGutters>
|
|
||||||
|
|
||||||
{editorMode === "insert" ? <CreateNewAsset type="building"/> : null}
|
|
||||||
<div
|
|
||||||
ref={canvasWrapperRef}
|
|
||||||
onDragOver={(e: React.DragEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.dataTransfer.dropEffect = "copy";
|
|
||||||
}}
|
|
||||||
onDrop={(e: React.DragEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const raw = e.dataTransfer.getData("text/plain");
|
|
||||||
console.log("DROP wrapper raw:", raw);
|
|
||||||
}}
|
|
||||||
style={{ height: "100%", width: "100%", position: "relative" }}
|
|
||||||
>
|
|
||||||
<canvas
|
|
||||||
ref={canvasRef}
|
|
||||||
onDragOver={onDragOver}
|
|
||||||
onDrop={onDrop}
|
|
||||||
style={{ height: "100%", width: "100%" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CreateNewAsset({ type }: { type: string }) {
|
|
||||||
return (
|
|
||||||
<div style={{ backgroundColor: "#F1E9DB", borderRadius: 20, height: 40, display: "flex", alignItems: "center", justifyContent: "center", columnGap: 20 }}>
|
|
||||||
{type === "building"
|
|
||||||
? buildingTypes.map((buildingType, i) => {
|
|
||||||
const label = buildingType[0].toUpperCase() + buildingType.slice(1);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
draggable
|
|
||||||
onDragStart={(e) => {
|
|
||||||
console.log("DRAG START", buildingType);
|
|
||||||
const payload = { kind: "building", type: buildingType, label, w: 160, h: 90 };
|
|
||||||
e.dataTransfer.setData("text/plain", JSON.stringify(payload));
|
|
||||||
e.dataTransfer.effectAllowed = "copy";
|
|
||||||
}}
|
|
||||||
style={{ display: "inline-flex" }}
|
|
||||||
>
|
|
||||||
<Chip sx={{ backgroundColor: "#5DB7DE" }} label={label} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
: unitTypes.map((unitType, i) => {
|
|
||||||
const label = unitType[0].toUpperCase() + unitType.slice(1);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
draggable
|
|
||||||
onDragStart={(e) => {
|
|
||||||
const payload = { kind: "unit", type: unitType, label, w: 140, h: 80 };
|
|
||||||
e.dataTransfer.setData("text/plain", JSON.stringify(payload));
|
|
||||||
e.dataTransfer.effectAllowed = "copy";
|
|
||||||
}}
|
|
||||||
style={{ display: "inline-flex" }}
|
|
||||||
>
|
|
||||||
<Chip sx={{ backgroundColor: "#5DB7DE" }} label={label} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
63
src/components/dev-tools/Editor.tsx
Normal file
63
src/components/dev-tools/Editor.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import React from "react";
|
||||||
|
import styles from "@/styles/dev-tools.module.css"
|
||||||
|
import { Container, Chip } from "@mui/material"
|
||||||
|
import { buildingTypes, unitTypes } from "@/types/dev-tools-extra";
|
||||||
|
import { useEditor } from "@/hooks/useEditor";
|
||||||
|
|
||||||
|
export default function Editor(){
|
||||||
|
// TODO: create an inspector (in HTML) and a node view (in canvas)
|
||||||
|
const { editorMode, currentDataId } = useEditor();
|
||||||
|
|
||||||
|
const canvasWrapperRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
|
const canvasRef = React.useRef<HTMLCanvasElement | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className={styles.editor} maxWidth={false} disableGutters>
|
||||||
|
|
||||||
|
{editorMode === "insert" ? <CreateNewAsset type="building"/> : null}
|
||||||
|
<div
|
||||||
|
ref={canvasWrapperRef}
|
||||||
|
style={{ height: "100%", width: "100%", position: "relative" }}
|
||||||
|
>
|
||||||
|
<canvas
|
||||||
|
ref={canvasRef}
|
||||||
|
style={{ height: "100%", width: "100%" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function CreateNewAsset({ type }: { type: string }) {
|
||||||
|
return (
|
||||||
|
<div style={{ backgroundColor: "#F1E9DB", borderRadius: 20, height: 40, display: "flex", alignItems: "center", justifyContent: "center", columnGap: 20 }}>
|
||||||
|
{type === "building"
|
||||||
|
? buildingTypes.map((buildingType, i) => {
|
||||||
|
const label = buildingType[0].toUpperCase() + buildingType.slice(1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
style={{ display: "inline-flex" }}
|
||||||
|
>
|
||||||
|
<Chip sx={{ backgroundColor: "#5DB7DE" }} label={label} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: unitTypes.map((unitType, i) => {
|
||||||
|
const label = unitType[0].toUpperCase() + unitType.slice(1);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
style={{ display: "inline-flex" }}
|
||||||
|
>
|
||||||
|
<Chip sx={{ backgroundColor: "#5DB7DE" }} label={label} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
62
src/components/dev-tools/LeftMenu.tsx
Normal file
62
src/components/dev-tools/LeftMenu.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import React from "react";
|
||||||
|
import styles from "@/styles/dev-tools.module.css"
|
||||||
|
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
|
||||||
|
import { Container, Select, Stack, MenuItem, InputLabel, Typography } from "@mui/material"
|
||||||
|
import { buildingTypes, unitTypes } from "@/types/dev-tools-extra";
|
||||||
|
import { useEditor } from "@/hooks/useEditor";
|
||||||
|
|
||||||
|
const debugCard = [
|
||||||
|
{
|
||||||
|
id: "terrain_forest",
|
||||||
|
label: "Forest",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: "terrain_haunted_forest",
|
||||||
|
label: "Haunted Forest",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'terrain_undead_candy_forest',
|
||||||
|
label: "Undead Candy Forest"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function LeftMenu(){
|
||||||
|
|
||||||
|
//const [themes, setThemes] = React.useState<ThemeSelection[]>([]); // get from the database
|
||||||
|
const [menuMode, setMenuMode] = React.useState<"unit" | "card">("card");
|
||||||
|
const { changeCurrentDataId } = useEditor();
|
||||||
|
|
||||||
|
const handleSelectAsset = (_e: React.MouseEvent<Element, MouseEvent>, id: string) => {
|
||||||
|
changeCurrentDataId!(id);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Container className={styles.leftmenu}>
|
||||||
|
<Stack >
|
||||||
|
<Typography id="assetTypeFilter">Asset Type</Typography>
|
||||||
|
<Select className="assetTypeFilter">
|
||||||
|
<MenuItem value="">All</MenuItem>
|
||||||
|
{menuMode === "card" ?
|
||||||
|
buildingTypes.map((opt, i) => (<MenuItem value={opt} key={i}>{opt[0].toUpperCase() + opt.slice(1)}</MenuItem>)) :
|
||||||
|
unitTypes.map((opt, i) => (<MenuItem value={opt} key={i}>{opt[0].toUpperCase() + opt.slice(1)}</MenuItem>))
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<InputLabel id="themeFilter">Theme</InputLabel>
|
||||||
|
<Select className="themeFilter" label="Theme" >
|
||||||
|
<MenuItem value="">All</MenuItem>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
</Stack>
|
||||||
|
<Typography >
|
||||||
|
Current {menuMode}s created
|
||||||
|
</Typography>
|
||||||
|
<RichTreeView items={debugCard} onItemClick={handleSelectAsset}>
|
||||||
|
</RichTreeView>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
28
src/contexts/EditorContext.tsx
Normal file
28
src/contexts/EditorContext.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Card, Unit, Combo } from "@/types/dev-tools"
|
||||||
|
import { createContext } from "react"
|
||||||
|
|
||||||
|
export interface EditorContextType {
|
||||||
|
editorMode: "insert" | "update";
|
||||||
|
currentRecordNum: number | null;
|
||||||
|
currentDataId: string | null;
|
||||||
|
currentCombos: Combo[] | null
|
||||||
|
cards: Card[] | null;
|
||||||
|
units: Unit[] | null;
|
||||||
|
error: string | null,
|
||||||
|
|
||||||
|
changeEditorMode?: (m: string) => void;
|
||||||
|
changeCurrentRecordNum?: (n: number) => void;
|
||||||
|
changeCurrentDataId?: (n: string) => void;
|
||||||
|
changeError?: (e: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const EditorContext = createContext<EditorContextType>({
|
||||||
|
currentRecordNum: null,
|
||||||
|
currentDataId: null,
|
||||||
|
editorMode: "insert",
|
||||||
|
cards: null,
|
||||||
|
units: null,
|
||||||
|
currentCombos: null,
|
||||||
|
error: null,
|
||||||
|
})
|
||||||
11
src/hooks/useEditor.ts
Normal file
11
src/hooks/useEditor.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { EditorContext } from "@/contexts/EditorContext"
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
export function useEditor() {
|
||||||
|
const ctx = useContext(EditorContext);
|
||||||
|
if(!ctx){
|
||||||
|
throw new Error("Error: use Editor must be used within an EditorContext")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
@@ -16,8 +16,6 @@ CREATE TABLE cards (
|
|||||||
created_timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
created_timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS units;
|
DROP TABLE IF EXISTS units;
|
||||||
DROP SEQUENCE IF EXISTS units_record_num_sequence;
|
DROP SEQUENCE IF EXISTS units_record_num_sequence;
|
||||||
DROP SEQUENCE IF EXISTS units_unit_id_sequence;
|
DROP SEQUENCE IF EXISTS units_unit_id_sequence;
|
||||||
@@ -42,6 +40,20 @@ CREATE TABLE units (
|
|||||||
created_timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
created_timestamp TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS combos;
|
||||||
|
DROP SEQUENCE IF EXISTS combos_record_num_sequence;
|
||||||
|
|
||||||
|
CREATE SEQUENCE combos_record_num_sequence;
|
||||||
|
|
||||||
|
CREATE TABLE combos (
|
||||||
|
record_num INTEGER PRIMARY KEY DEFAULT nextval('combos_record_num_sequence'),
|
||||||
|
parent_data_id VARCHAR(100) NOT NULL,
|
||||||
|
combo_data_id VARCHAR(100) NOT NULL,
|
||||||
|
result_data_id VARCHAR(100) NOT NULL,
|
||||||
|
CHECK (parent_data_id <> result_data_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS resources;
|
DROP TABLE IF EXISTS resources;
|
||||||
DROP SEQUENCE IF EXISTS resources_record_num_sequence;
|
DROP SEQUENCE IF EXISTS resources_record_num_sequence;
|
||||||
DROP SEQUENCE IF EXISTS resources_resource_id_sequence;
|
DROP SEQUENCE IF EXISTS resources_resource_id_sequence;
|
||||||
|
|||||||
5
src/types/dev-tools-extra.ts
Normal file
5
src/types/dev-tools-extra.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
export const unitTypes: string[] = ["minion", "enemy", "hero", "ally"];
|
||||||
|
export const buildingTypes: string[] = ["enemy", "minion", "terrain", "support"];
|
||||||
|
|
||||||
|
|
||||||
28
src/types/dev-tools.d.ts
vendored
Normal file
28
src/types/dev-tools.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
export type EditorMode = "insert" | "update";
|
||||||
|
|
||||||
|
export interface Combo {
|
||||||
|
parent_data_id: string;
|
||||||
|
combo_data_id: string;
|
||||||
|
result_data_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DBObject {
|
||||||
|
record_num: number
|
||||||
|
data_id: string;
|
||||||
|
change_timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Unit extends DBObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Card extends DBObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface ThemeSelection {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user