diff --git a/src/assets/github.png b/src/assets/github.png
new file mode 100644
index 0000000..a3a73d6
Binary files /dev/null and b/src/assets/github.png differ
diff --git a/src/assets/google.png b/src/assets/google.png
new file mode 100644
index 0000000..fa72a27
Binary files /dev/null and b/src/assets/google.png differ
diff --git a/src/components/table.jsx b/src/components/table.jsx
index b21e95a..8c8bd45 100644
--- a/src/components/table.jsx
+++ b/src/components/table.jsx
@@ -100,9 +100,9 @@ export default function Table(props) {
? props.tableData.name.length < 10
? props.tableData.name
: `${props.tableData.name.substring(0, 10)}...`
- : props.tableData.name.length < 14
+ : props.tableData.name.length < 16
? props.tableData.name
- : `${props.tableData.name.substring(0, 14)}...`}
+ : `${props.tableData.name.substring(0, 16)}...`}
{isHovered && (
diff --git a/src/pages/signup.jsx b/src/pages/signup.jsx
index 402eec2..650ed00 100644
--- a/src/pages/signup.jsx
+++ b/src/pages/signup.jsx
@@ -1,20 +1,335 @@
-import { useEffect, useState } from "react";
+import { useEffect, useState, useRef } from "react";
import { Link } from "react-router-dom";
-import logo from "../assets/logo_light_46.png";
-import ReCAPTCHA from "react-google-recaptcha";
-import { IconEyeClosedSolid, IconEyeOpened } from "@douyinfe/semi-icons";
-import { Banner } from "@douyinfe/semi-ui";
+import logo from "../assets/icon_dark_64.png";
+import google_logo from "../assets/google.png";
+import github_logo from "../assets/github.png";
import axios from "axios";
+import { Cardinality } from "../data/data";
+import { calcPath } from "../utils";
+import { Toast } from "@douyinfe/semi-ui";
+
+const xOffset = window.innerWidth * 0.42 * 0.15;
+const diagram = {
+ tables: [
+ {
+ name: "galactic_users",
+ x: xOffset + 101,
+ y: window.innerHeight * 0.75 - (4 * 36 + 50 + 7) * 0.5,
+ fields: [
+ {
+ name: "id",
+ type: "INT",
+ },
+ {
+ name: "username",
+ type: "VARCHAR",
+ },
+ {
+ name: "email",
+ type: "VARCHAR",
+ },
+ {
+ name: "password",
+ type: "VARCHAR",
+ },
+ ],
+ color: "#7d9dff",
+ },
+ {
+ name: "celestial_data",
+ x: xOffset,
+ y: window.innerHeight * 0.32 - (5 * 36 + 50 + 7) * 0.5,
+ fields: [
+ {
+ name: "id",
+ type: "INT",
+ },
+ {
+ name: "user_id",
+ type: "INT",
+ },
+ {
+ name: "type",
+ type: "ENUM",
+ },
+ {
+ name: "time",
+ type: "TIMESTAMP",
+ },
+ {
+ name: "content",
+ type: "VARCHAR",
+ },
+ ],
+ color: "#89e667",
+ },
+ ],
+ relationships: [
+ {
+ startTableId: 1,
+ startFieldId: 1,
+ endTableId: 0,
+ endFieldId: 0,
+ startX: xOffset + 16,
+ startY:
+ window.innerHeight * 0.32 - (4 * 36 + 50 + 7) * 0.5 + (50 + 18 * 2),
+ endX: xOffset + 115,
+ endY: window.innerHeight * 0.75 - (4 * 36 + 50 + 7) * 0.5 + (50 + 18 * 1),
+ cardinality: "One to one",
+ },
+ ],
+};
+
+function Table({ table, grab }) {
+ const [isHovered, setIsHovered] = useState(false);
+ const [hoveredField, setHoveredField] = useState(-1);
+ const height = table.fields.length * 36 + 50 + 7;
+ return (
+
setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ >
+
+
+
+ {table.name}
+
+ {table.fields.map((e, i) => (
+
setHoveredField(i)}
+ onMouseLeave={() => setHoveredField(-1)}
+ >
+
+
+ {e.name}
+
+
{e.type}
+
+ ))}
+
+
+ );
+}
+
+function Relationship({ relationship }) {
+ const pathRef = useRef();
+ let start = { x: 0, y: 0 };
+ let end = { x: 0, y: 0 };
+
+ let cardinalityStart = "1";
+ let cardinalityEnd = "1";
+
+ switch (relationship.cardinality) {
+ case Cardinality.MANY_TO_ONE:
+ cardinalityStart = "n";
+ cardinalityEnd = "1";
+ break;
+ case Cardinality.ONE_TO_MANY:
+ cardinalityStart = "1";
+ cardinalityEnd = "n";
+ break;
+ case Cardinality.ONE_TO_ONE:
+ cardinalityStart = "1";
+ cardinalityEnd = "1";
+ break;
+ default:
+ break;
+ }
+
+ const length = 32;
+
+ const [refAquired, setRefAquired] = useState(false);
+ useEffect(() => {
+ setRefAquired(true);
+ }, []);
+
+ if (refAquired) {
+ const pathLength = pathRef.current.getTotalLength();
+ const point1 = pathRef.current.getPointAtLength(length);
+ start = { x: point1.x, y: point1.y };
+ const point2 = pathRef.current.getPointAtLength(pathLength - length);
+ end = { x: point2.x, y: point2.y };
+ }
+
+ return (
+
console.log(pathRef.current)}>
+
+ {pathRef.current && (
+ <>
+
+
+ {cardinalityStart}
+
+
+
+ {cardinalityEnd}
+
+ >
+ )}
+
+ );
+}
+
+function Canvas() {
+ const [tables, setTables] = useState(diagram.tables);
+ const [relationships, setRelationships] = useState(diagram.relationships);
+ const [dragging, setDragging] = useState(-1);
+ const [offset, setOffset] = useState({ x: 0, y: 0 });
+
+ const grabTable = (e, id) => {
+ setDragging(id);
+ setOffset({
+ x: e.clientX - tables[id].x,
+ y: e.clientY - tables[id].y,
+ });
+ };
+
+ const moveTable = (e) => {
+ if (dragging !== -1) {
+ const dx = e.clientX - offset.x;
+ const dy = e.clientY - offset.y;
+ setTables((prev) =>
+ prev.map((table, i) => {
+ if (i === dragging) {
+ setRelationships((prev) =>
+ prev.map((r) => {
+ if (r.startTableId === i) {
+ return {
+ ...r,
+ startX: dx + 15,
+ startY: dy + r.startFieldId * 36 + 69,
+ endX: tables[r.endTableId].x + 15,
+ endY: tables[r.endTableId].y + r.endFieldId * 36 + 69,
+ };
+ } else if (r.endTableId === i) {
+ return {
+ ...r,
+ startX: tables[r.startTableId].x + 15,
+ startY: tables[r.startTableId].y + r.startFieldId * 36 + 69,
+ endX: dx + 15,
+ endY: dy + r.endFieldId * 36 + 69,
+ };
+ }
+ return r;
+ })
+ );
+
+ return {
+ ...table,
+ x: dx,
+ y: dy,
+ };
+ }
+ return table;
+ })
+ );
+ }
+ };
+
+ const releaseTable = () => {
+ setDragging(-1);
+ setOffset({ x: 0, y: 0 });
+ };
+
+ return (
+