cleaning up UI
This commit is contained in:
@@ -35,6 +35,11 @@
|
||||
"static/chunks/webpack.js",
|
||||
"static/chunks/main-app.js",
|
||||
"static/chunks/app/gunbuilder/admin/category-mappings/page.js"
|
||||
],
|
||||
"/page": [
|
||||
"static/chunks/webpack.js",
|
||||
"static/chunks/main-app.js",
|
||||
"static/chunks/app/page.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"/gunbuilder/admin/merchants/page": "app/gunbuilder/admin/merchants/page.js",
|
||||
"/gunbuilder/build/page": "app/gunbuilder/build/page.js",
|
||||
"/gunbuilder/page": "app/gunbuilder/page.js",
|
||||
"/gunbuilder/[categoryId]/page": "app/gunbuilder/[categoryId]/page.js",
|
||||
"/gunbuilder/admin/category-mappings/page": "app/gunbuilder/admin/category-mappings/page.js",
|
||||
"/gunbuilder/[categoryId]/[partId]/page": "app/gunbuilder/[categoryId]/[partId]/page.js"
|
||||
"/page": "app/page.js"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1975,6 +1975,17 @@ eval("\nObject.defineProperty(exports, \"__esModule\", ({\n value: true\n}));
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(rsc)/./node_modules/next/dist/api/link.js":
|
||||
/*!********************************************!*\
|
||||
!*** ./node_modules/next/dist/api/link.js ***!
|
||||
\********************************************/
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
"use strict";
|
||||
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* reexport default from dynamic */ _client_link__WEBPACK_IMPORTED_MODULE_0___default.a)\n/* harmony export */ });\n/* harmony import */ var _client_link__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../client/link */ \"(rsc)/./node_modules/next/dist/client/link.js\");\n/* harmony import */ var _client_link__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_client_link__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};\n/* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _client_link__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== \"default\") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _client_link__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__]\n/* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);\n\n\n\n//# sourceMappingURL=link.js.map//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHJzYykvLi9ub2RlX21vZHVsZXMvbmV4dC9kaXN0L2FwaS9saW5rLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUF5QztBQUNWOztBQUUvQiIsInNvdXJjZXMiOlsid2VicGFjazovL2d1bmJ1aWxkZXItcHJvdG90eXBlLy4vbm9kZV9tb2R1bGVzL25leHQvZGlzdC9hcGkvbGluay5qcz9lZmQ0Il0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IGRlZmF1bHQgfSBmcm9tIFwiLi4vY2xpZW50L2xpbmtcIjtcbmV4cG9ydCAqIGZyb20gXCIuLi9jbGllbnQvbGlua1wiO1xuXG4vLyMgc291cmNlTWFwcGluZ1VSTD1saW5rLmpzLm1hcCJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///(rsc)/./node_modules/next/dist/api/link.js\n");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(rsc)/./node_modules/next/dist/build/output/log.js":
|
||||
/*!****************************************************!*\
|
||||
!*** ./node_modules/next/dist/build/output/log.js ***!
|
||||
@@ -2129,6 +2140,21 @@ eval("\nObject.defineProperty(exports, \"__esModule\", ({\n value: true\n}));
|
||||
"use strict";
|
||||
eval("\nObject.defineProperty(exports, \"__esModule\", ({\n value: true\n}));\n0 && (0);\nfunction _export(target, all) {\n for(var name in all)Object.defineProperty(target, name, {\n enumerable: true,\n get: all[name]\n });\n}\n_export(exports, {\n StaticGenBailoutError: function() {\n return StaticGenBailoutError;\n },\n isStaticGenBailoutError: function() {\n return isStaticGenBailoutError;\n }\n});\nconst NEXT_STATIC_GEN_BAILOUT = \"NEXT_STATIC_GEN_BAILOUT\";\nclass StaticGenBailoutError extends Error {\n constructor(...args){\n super(...args);\n this.code = NEXT_STATIC_GEN_BAILOUT;\n }\n}\nfunction isStaticGenBailoutError(error) {\n if (typeof error !== \"object\" || error === null || !(\"code\" in error)) {\n return false;\n }\n return error.code === NEXT_STATIC_GEN_BAILOUT;\n}\nif ((typeof exports.default === \"function\" || typeof exports.default === \"object\" && exports.default !== null) && typeof exports.default.__esModule === \"undefined\") {\n Object.defineProperty(exports.default, \"__esModule\", {\n value: true\n });\n Object.assign(exports.default, exports);\n module.exports = exports.default;\n} //# sourceMappingURL=static-generation-bailout.js.map\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKHJzYykvLi9ub2RlX21vZHVsZXMvbmV4dC9kaXN0L2NsaWVudC9jb21wb25lbnRzL3N0YXRpYy1nZW5lcmF0aW9uLWJhaWxvdXQuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0lBRWFBLHVCQUFxQjtlQUFyQkE7O0lBSUdDLHlCQUF1QjtlQUF2QkE7OztBQU5oQixNQUFNQywwQkFBMEI7QUFFekIsTUFBTUYsOEJBQThCRzs7O2FBQ3pCQyxJQUFBQSxHQUFPRjs7QUFDekI7QUFFTyxTQUFTRCx3QkFDZEksS0FBYztJQUVkLElBQUksT0FBT0EsVUFBVSxZQUFZQSxVQUFVLFFBQVEsQ0FBRSxXQUFVQSxLQUFBQSxHQUFRO1FBQ3JFLE9BQU87SUFDVDtJQUVBLE9BQU9BLE1BQU1ELElBQUksS0FBS0Y7QUFDeEIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9ndW5idWlsZGVyLXByb3RvdHlwZS8uLi8uLi8uLi9zcmMvY2xpZW50L2NvbXBvbmVudHMvc3RhdGljLWdlbmVyYXRpb24tYmFpbG91dC50cz80YzMxIl0sIm5hbWVzIjpbIlN0YXRpY0dlbkJhaWxvdXRFcnJvciIsImlzU3RhdGljR2VuQmFpbG91dEVycm9yIiwiTkVYVF9TVEFUSUNfR0VOX0JBSUxPVVQiLCJFcnJvciIsImNvZGUiLCJlcnJvciJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(rsc)/./node_modules/next/dist/client/components/static-generation-bailout.js\n");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(rsc)/./node_modules/next/dist/client/link.js":
|
||||
/*!***********************************************!*\
|
||||
!*** ./node_modules/next/dist/client/link.js ***!
|
||||
\***********************************************/
|
||||
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
||||
|
||||
"use strict";
|
||||
/* __next_internal_client_entry_do_not_use__ cjs */
|
||||
const { createProxy } = __webpack_require__(/*! next/dist/build/webpack/loaders/next-flight-loader/module-proxy */ "(rsc)/./node_modules/next/dist/build/webpack/loaders/next-flight-loader/module-proxy.js");
|
||||
module.exports = createProxy("/Users/sean/Dev/gunbuilder-prototype/node_modules/next/dist/client/link.js");
|
||||
//# sourceMappingURL=link.js.map
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "(rsc)/./node_modules/next/dist/compiled/@opentelemetry/api/index.js":
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
/******/
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.h = () => ("e54948a0e485cc1b")
|
||||
/******/ __webpack_require__.h = () => ("2634c5f25c171b75")
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,7 +25,7 @@ eval(__webpack_require__.ts("Promise.resolve(/*! import() eager */).then(__webpa
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"c92bc8e6af0b\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJjOTJiYzhlNmFmMGJcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"199a90062aed\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCIxOTlhOTAwNjJhZWRcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -192,7 +192,7 @@
|
||||
/******/
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "7f1da3c97852a23d"; }
|
||||
/******/ __webpack_require__.h = function() { return "a5891ef4daa4c372"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/global */
|
||||
|
||||
@@ -675,6 +675,9 @@ video {
|
||||
.items-baseline {
|
||||
align-items: baseline;
|
||||
}
|
||||
.items-stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
@@ -908,6 +911,9 @@ video {
|
||||
.bg-amber-500\/5 {
|
||||
background-color: rgb(245 158 11 / 0.05);
|
||||
}
|
||||
.bg-zinc-900\/80 {
|
||||
background-color: rgb(24 24 27 / 0.8);
|
||||
}
|
||||
.object-contain {
|
||||
-o-object-fit: contain;
|
||||
object-fit: contain;
|
||||
@@ -1185,6 +1191,10 @@ video {
|
||||
--tw-shadow-colored: 0 0 0 1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
.outline-none {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.filter {
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
@@ -1255,6 +1265,10 @@ body {
|
||||
background-color: rgb(39 39 42 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-zinc-900\/60:hover {
|
||||
background-color: rgb(24 24 27 / 0.6);
|
||||
}
|
||||
|
||||
.hover\:text-amber-200:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(253 230 138 / var(--tw-text-opacity));
|
||||
@@ -1297,10 +1311,18 @@ body {
|
||||
|
||||
@media (min-width: 640px) {
|
||||
|
||||
.sm\:w-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.sm\:flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sm\:flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sm\:items-center {
|
||||
align-items: center;
|
||||
}
|
||||
@@ -1316,10 +1338,18 @@ body {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.md\:mt-5 {
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.md\:inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.md\:inline-flex {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.md\:grid {
|
||||
display: grid;
|
||||
}
|
||||
@@ -1328,6 +1358,22 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md\:w-auto {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.md\:w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.md\:flex-1 {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.md\:flex-none {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.md\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
@@ -1356,6 +1402,10 @@ body {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.md\:justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.md\:justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"c":["app/layout","app/gunbuilder/page","webpack"],"r":[],"m":[]}
|
||||
@@ -0,0 +1 @@
|
||||
{"c":["app/layout","app/gunbuilder/page","webpack"],"r":[],"m":[]}
|
||||
@@ -0,0 +1 @@
|
||||
{"c":["app/layout","app/gunbuilder/page","webpack"],"r":[],"m":[]}
|
||||
@@ -0,0 +1 @@
|
||||
{"c":["app/layout","app/gunbuilder/page","webpack"],"r":[],"m":["(app-pages-browser)/./components/CategoryColumn.tsx","(app-pages-browser)/./components/PartCard.tsx"]}
|
||||
@@ -0,0 +1 @@
|
||||
{"c":["webpack"],"r":[],"m":[]}
|
||||
@@ -0,0 +1 @@
|
||||
{"c":["app/layout","app/gunbuilder/page","webpack"],"r":[],"m":[]}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||
|
||||
/***/ "(app-pages-browser)/./app/globals.css":
|
||||
/*!*************************!*\
|
||||
!*** ./app/globals.css ***!
|
||||
\*************************/
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"d47b578f5084\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJkNDdiNTc4ZjUwODRcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||
|
||||
/***/ "(app-pages-browser)/./app/globals.css":
|
||||
/*!*************************!*\
|
||||
!*** ./app/globals.css ***!
|
||||
\*************************/
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"199a90062aed\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCIxOTlhOTAwNjJhZWRcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||
|
||||
/***/ "(app-pages-browser)/./app/globals.css":
|
||||
/*!*************************!*\
|
||||
!*** ./app/globals.css ***!
|
||||
\*************************/
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"ef6b2a66502b\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJlZjZiMmE2NjUwMmJcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||
|
||||
/***/ "(app-pages-browser)/./app/globals.css":
|
||||
/*!*************************!*\
|
||||
!*** ./app/globals.css ***!
|
||||
\*************************/
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"48140ed43b0c\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCI0ODE0MGVkNDNiMGNcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||
|
||||
/***/ "(app-pages-browser)/./app/globals.css":
|
||||
/*!*************************!*\
|
||||
!*** ./app/globals.css ***!
|
||||
\*************************/
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"7c6f551e647b\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCI3YzZmNTUxZTY0N2JcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||
|
||||
/***/ "(app-pages-browser)/./app/globals.css":
|
||||
/*!*************************!*\
|
||||
!*** ./app/globals.css ***!
|
||||
\*************************/
|
||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||
|
||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"17661674de5c\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCIxNzY2MTY3NGRlNWNcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||
|
||||
/***/ })
|
||||
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"c":["app/layout","webpack"],"r":[],"m":[]}
|
||||
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "9785c6eec49ce244"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "a5891ef4daa4c372"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "5102201ef016c567"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "a9157d73caee028d"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "f1e3cb3a23a5445c"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "690496705c39ffdb"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
/*
|
||||
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||
* This devtool is neither made for production nor for readable output files.
|
||||
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||
* or disable the default devtool with "devtool: false".
|
||||
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||
*/
|
||||
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ !function() {
|
||||
/******/ __webpack_require__.h = function() { return "61383b8ade4329e6"; }
|
||||
/******/ }();
|
||||
/******/
|
||||
/******/ }
|
||||
);
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,79 @@
|
||||
// File: /Users/sean/Dev/gunbuilder-prototype/app/page.tsx
|
||||
import * as entry from '../../../app/page.js'
|
||||
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
|
||||
|
||||
type TEntry = typeof import('../../../app/page.js')
|
||||
|
||||
// Check that the entry is a valid entry
|
||||
checkFields<Diff<{
|
||||
default: Function
|
||||
config?: {}
|
||||
generateStaticParams?: Function
|
||||
revalidate?: RevalidateRange<TEntry> | false
|
||||
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
|
||||
dynamicParams?: boolean
|
||||
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
|
||||
preferredRegion?: 'auto' | 'global' | 'home' | string | string[]
|
||||
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
|
||||
maxDuration?: number
|
||||
|
||||
metadata?: any
|
||||
generateMetadata?: Function
|
||||
viewport?: any
|
||||
generateViewport?: Function
|
||||
|
||||
}, TEntry, ''>>()
|
||||
|
||||
// Check the prop type of the entry function
|
||||
checkFields<Diff<PageProps, FirstArg<TEntry['default']>, 'default'>>()
|
||||
|
||||
// Check the arguments and return type of the generateMetadata function
|
||||
if ('generateMetadata' in entry) {
|
||||
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
|
||||
checkFields<Diff<ResolvingMetadata, SecondArg<MaybeField<TEntry, 'generateMetadata'>>, 'generateMetadata'>>()
|
||||
}
|
||||
|
||||
// Check the arguments and return type of the generateViewport function
|
||||
if ('generateViewport' in entry) {
|
||||
checkFields<Diff<PageProps, FirstArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
|
||||
checkFields<Diff<ResolvingViewport, SecondArg<MaybeField<TEntry, 'generateViewport'>>, 'generateViewport'>>()
|
||||
}
|
||||
|
||||
// Check the arguments and return type of the generateStaticParams function
|
||||
if ('generateStaticParams' in entry) {
|
||||
checkFields<Diff<{ params: PageParams }, FirstArg<MaybeField<TEntry, 'generateStaticParams'>>, 'generateStaticParams'>>()
|
||||
checkFields<Diff<{ __tag__: 'generateStaticParams', __return_type__: any[] | Promise<any[]> }, { __tag__: 'generateStaticParams', __return_type__: ReturnType<MaybeField<TEntry, 'generateStaticParams'>> }>>()
|
||||
}
|
||||
|
||||
type PageParams = any
|
||||
export interface PageProps {
|
||||
params?: any
|
||||
searchParams?: any
|
||||
}
|
||||
export interface LayoutProps {
|
||||
children?: React.ReactNode
|
||||
|
||||
params?: any
|
||||
}
|
||||
|
||||
// =============
|
||||
// Utility types
|
||||
type RevalidateRange<T> = T extends { revalidate: any } ? NonNegative<T['revalidate']> : never
|
||||
|
||||
// If T is unknown or any, it will be an empty {} type. Otherwise, it will be the same as Omit<T, keyof Base>.
|
||||
type OmitWithTag<T, K extends keyof any, _M> = Omit<T, K>
|
||||
type Diff<Base, T extends Base, Message extends string = ''> = 0 extends (1 & T) ? {} : OmitWithTag<T, keyof Base, Message>
|
||||
|
||||
type FirstArg<T extends Function> = T extends (...args: [infer T, any]) => any ? unknown extends T ? any : T : never
|
||||
type SecondArg<T extends Function> = T extends (...args: [any, infer T]) => any ? unknown extends T ? any : T : never
|
||||
type MaybeField<T, K extends string> = T extends { [k in K]: infer G } ? G extends Function ? G : never : never
|
||||
|
||||
|
||||
|
||||
function checkFields<_ extends { [k in keyof any]: never }>() {}
|
||||
|
||||
// https://github.com/sindresorhus/type-fest
|
||||
type Numeric = number | bigint
|
||||
type Zero = 0 | 0n
|
||||
type Negative<T extends Numeric> = T extends Zero ? never : `${T}` extends `-${string}` ? T : never
|
||||
type NonNegative<T extends Numeric> = T extends Zero ? T : Negative<T> extends never ? T : '__invalid_negative_number__'
|
||||
+378
-194
@@ -5,7 +5,6 @@ import Link from "next/link";
|
||||
import { useSearchParams, useRouter } from "next/navigation";
|
||||
import { CATEGORIES } from "@/data/gunbuilderParts";
|
||||
import type { CategoryId, Part } from "@/types/gunbuilder";
|
||||
import { CategoryColumn } from "@/components/CategoryColumn";
|
||||
import { PART_ROLE_TO_CATEGORY } from "@/data/partRoleMappings";
|
||||
|
||||
type GunbuilderProductFromApi = {
|
||||
@@ -22,16 +21,11 @@ type GunbuilderProductFromApi = {
|
||||
const API_BASE_URL =
|
||||
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
|
||||
|
||||
|
||||
type BuildState = Partial<Record<CategoryId, string>>; // categoryId -> partId
|
||||
|
||||
const STORAGE_KEY = "gunbuilder-build-state";
|
||||
|
||||
// Group categories into logical sections.
|
||||
//
|
||||
// IMPORTANT:
|
||||
// These IDs must match the ids you have in CATEGORIES.
|
||||
// Anything that doesn't exist will just be skipped.
|
||||
// Category groups for the main grid
|
||||
const CATEGORY_GROUPS: {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -74,6 +68,13 @@ const CATEGORY_GROUPS: {
|
||||
"optic",
|
||||
],
|
||||
},
|
||||
// Optional: future group for accessories/furniture if needed
|
||||
// {
|
||||
// id: "accessories-group",
|
||||
// label: "Accessories & Furniture",
|
||||
// description: "Optics, lights, slings, and other supporting gear.",
|
||||
// categoryIds: ["optic", "sights", ...] as CategoryId[],
|
||||
// },
|
||||
];
|
||||
|
||||
export default function GunbuilderPage() {
|
||||
@@ -95,7 +96,8 @@ export default function GunbuilderPage() {
|
||||
}
|
||||
return {};
|
||||
});
|
||||
const [layoutMode, setLayoutMode] = useState<"cards" | "list">("cards");
|
||||
const [shareStatus, setShareStatus] = useState<string | null>(null);
|
||||
const [shareUrl, setShareUrl] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
@@ -203,6 +205,28 @@ export default function GunbuilderPage() {
|
||||
[selectedParts],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
// If no parts selected, clear the share URL
|
||||
if (Object.keys(build).length === 0) {
|
||||
setShareUrl("");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = JSON.stringify(build);
|
||||
const encoded = window.btoa(payload);
|
||||
const origin = window.location?.origin ?? "";
|
||||
const url = `${origin}/gunbuilder/build?build=${encodeURIComponent(
|
||||
encoded,
|
||||
)}`;
|
||||
setShareUrl(url);
|
||||
} catch {
|
||||
setShareUrl("");
|
||||
}
|
||||
}, [build]);
|
||||
|
||||
const handleSelectPart = (categoryId: CategoryId, partId: string) => {
|
||||
setBuild((prev) => ({
|
||||
...prev,
|
||||
@@ -219,6 +243,88 @@ export default function GunbuilderPage() {
|
||||
[],
|
||||
);
|
||||
|
||||
const handleShare = async () => {
|
||||
if (selectedParts.length === 0) {
|
||||
setShareStatus("Add a few parts before sharing your build.");
|
||||
return;
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push("Shadow Standard — The Armory Build");
|
||||
lines.push("");
|
||||
lines.push(`Total: $${totalPrice.toFixed(2)}`);
|
||||
lines.push("");
|
||||
|
||||
summaryCategoryOrder.forEach((categoryId) => {
|
||||
const cat = CATEGORIES.find((c) => c.id === categoryId);
|
||||
if (!cat) return;
|
||||
|
||||
const selectedPartId = build[cat.id];
|
||||
const part = parts.find(
|
||||
(p) => p.id === selectedPartId && p.categoryId === cat.id,
|
||||
);
|
||||
|
||||
if (part) {
|
||||
lines.push(
|
||||
`${cat.name}: ${part.brand} — ${part.name} ($${part.price.toFixed(
|
||||
2,
|
||||
)})`,
|
||||
);
|
||||
} else {
|
||||
lines.push(`${cat.name}: [Not selected]`);
|
||||
}
|
||||
});
|
||||
|
||||
const text = lines.join("\n");
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setShareStatus("Build summary copied to clipboard.");
|
||||
} catch {
|
||||
setShareStatus(
|
||||
"Could not access clipboard. You can copy from the build summary page.",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopyLink = async () => {
|
||||
if (!shareUrl) {
|
||||
setShareStatus("No shareable link available yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(shareUrl);
|
||||
setShareStatus("Shareable link copied to clipboard.");
|
||||
} catch {
|
||||
setShareStatus("Could not copy link to clipboard.");
|
||||
}
|
||||
};
|
||||
|
||||
const handleNativeShare = async () => {
|
||||
if (!shareUrl) {
|
||||
setShareStatus("No shareable link available yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share({
|
||||
title: "Shadow Standard — The Armory Build",
|
||||
text: "Check out my Shadow Standard Armory build.",
|
||||
url: shareUrl,
|
||||
});
|
||||
setShareStatus("Share dialog opened.");
|
||||
} catch {
|
||||
// User canceled or share failed; keep it quiet but let them know
|
||||
setShareStatus("Share canceled or unavailable. You can copy the link instead.");
|
||||
}
|
||||
} else {
|
||||
// Fallback to copying the link
|
||||
await handleCopyLink();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-black text-zinc-50">
|
||||
<div className="border-b border-amber-500/20 bg-amber-500/5">
|
||||
@@ -239,7 +345,7 @@ export default function GunbuilderPage() {
|
||||
</div>
|
||||
<div className="mx-auto max-w-6xl px-4 py-6 lg:py-10">
|
||||
{/* Header */}
|
||||
<header className="mb-6 flex flex-col gap-2 md:flex-row md:items-end md:justify-between">
|
||||
<header className="mb-6">
|
||||
<div>
|
||||
<p className="text-xs font-semibold tracking-[0.2em] uppercase text-zinc-500">
|
||||
Shadow Standard
|
||||
@@ -254,189 +360,30 @@ export default function GunbuilderPage() {
|
||||
locally so you can come back and refine it anytime.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 md:mt-0">
|
||||
<div className="text-xs text-zinc-400">Current build total</div>
|
||||
<div className="text-2xl font-semibold text-amber-300">
|
||||
${totalPrice.toFixed(2)}
|
||||
</div>
|
||||
<div className="text-xs text-zinc-500">
|
||||
{selectedParts.length} / {CATEGORIES.length} categories filled
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Layout */}
|
||||
<div className="grid gap-4 lg:grid-cols-[3fr,1.25fr]">
|
||||
{/* Categories/parts */}
|
||||
<section className="rounded-lg border border-zinc-800 bg-zinc-950/60 p-3 md:p-4">
|
||||
<div className="mb-4 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-xs text-zinc-500">
|
||||
Choose one part per category. Switch layouts to browse by cards or a tighter list view.
|
||||
</p>
|
||||
<div className="inline-flex items-center rounded-md border border-zinc-700 bg-zinc-900/70 text-[0.7rem]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLayoutMode("cards")}
|
||||
className={`px-2.5 py-1 rounded-l-md border-r border-zinc-700 transition-colors ${
|
||||
layoutMode === "cards"
|
||||
? "bg-amber-500/20 text-amber-200"
|
||||
: "text-zinc-400 hover:bg-zinc-800"
|
||||
}`}
|
||||
>
|
||||
Card view
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLayoutMode("list")}
|
||||
className={`px-2.5 py-1 rounded-r-md transition-colors ${
|
||||
layoutMode === "list"
|
||||
? "bg-amber-500/20 text-amber-200"
|
||||
: "text-zinc-400 hover:bg-zinc-800"
|
||||
}`}
|
||||
>
|
||||
List view
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{loading && (
|
||||
<p className="text-sm text-zinc-500">
|
||||
Loading parts from backend…
|
||||
</p>
|
||||
)}
|
||||
{error && !loading && (
|
||||
<p className="text-sm text-red-400">
|
||||
{error} — check that the Ballistic API is running and CORS is
|
||||
configured.
|
||||
</p>
|
||||
)}
|
||||
{!loading && !error && (
|
||||
<div className="space-y-6">
|
||||
{CATEGORY_GROUPS.map((group) => {
|
||||
const groupCategories = CATEGORIES.filter((c) =>
|
||||
group.categoryIds.includes(c.id as CategoryId),
|
||||
);
|
||||
|
||||
if (groupCategories.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={group.id} className="space-y-3">
|
||||
<div className="flex items-baseline justify-between gap-2">
|
||||
<div>
|
||||
<h2 className="text-sm font-semibold text-zinc-100">
|
||||
{group.label}
|
||||
</h2>
|
||||
{group.description && (
|
||||
<p className="text-xs text-zinc-500 mt-1 max-w-xl">
|
||||
{group.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{layoutMode === "cards" ? (
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{groupCategories.map((category) => (
|
||||
<div
|
||||
key={category.id}
|
||||
className="border border-zinc-800 rounded-md p-2 md:p-3 bg-zinc-950/60"
|
||||
>
|
||||
<CategoryColumn
|
||||
category={category}
|
||||
parts={partsByCategory[category.id]}
|
||||
selectedPartId={build[category.id]}
|
||||
onSelectPart={(partId) =>
|
||||
handleSelectPart(category.id, partId)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{groupCategories.map((category) => (
|
||||
<div
|
||||
key={category.id}
|
||||
className="border border-zinc-800 rounded-md p-2 md:p-3 bg-zinc-950/60 flex flex-col md:flex-row md:items-start md:justify-between gap-3"
|
||||
>
|
||||
<CategoryColumn
|
||||
category={category}
|
||||
parts={partsByCategory[category.id]}
|
||||
selectedPartId={build[category.id]}
|
||||
onSelectPart={(partId) =>
|
||||
handleSelectPart(category.id, partId)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Build Summary */}
|
||||
<aside className="rounded-lg border border-zinc-800 bg-zinc-950/80 p-3 md:p-4 flex flex-col">
|
||||
<h2 className="text-xs font-semibold tracking-[0.16em] uppercase text-zinc-400 mb-3">
|
||||
Current Build
|
||||
{/* Build summary panel */}
|
||||
<section className="mb-6 rounded-lg border border-zinc-800 bg-zinc-950/80 p-3 md:p-4 flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
{/* Left: title + totals + primary actions */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<h2 className="text-xs font-semibold uppercase tracking-[0.16em] text-amber-300">
|
||||
My Build Breakdown
|
||||
</h2>
|
||||
|
||||
<div className="flex-1 overflow-y-auto pr-1">
|
||||
{selectedParts.length === 0 && (
|
||||
<p className="text-sm text-zinc-500">
|
||||
Select a part from each category to start your build.
|
||||
</p>
|
||||
)}
|
||||
|
||||
<ul className="space-y-2">
|
||||
{summaryCategoryOrder.map((categoryId) => {
|
||||
const cat = CATEGORIES.find((c) => c.id === categoryId);
|
||||
if (!cat) return null;
|
||||
|
||||
const selectedPartId = build[cat.id];
|
||||
const part = parts.find(
|
||||
(p) =>
|
||||
p.id === selectedPartId && p.categoryId === cat.id,
|
||||
);
|
||||
|
||||
return (
|
||||
<li key={cat.id}>
|
||||
<div className="text-[0.7rem] uppercase tracking-[0.16em] text-zinc-500">
|
||||
{cat.name}
|
||||
</div>
|
||||
{part ? (
|
||||
<div className="mt-1 flex justify-between items-center gap-2">
|
||||
<div className="text-sm text-zinc-50">
|
||||
<span className="text-zinc-400">
|
||||
{part.brand}{" "}
|
||||
</span>
|
||||
{part.name}
|
||||
</div>
|
||||
<div className="text-xs font-semibold text-amber-300 whitespace-nowrap">
|
||||
${part.price.toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-1 text-xs text-zinc-600">
|
||||
Not selected
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<div>
|
||||
<div className="text-xs text-zinc-400">Current build total</div>
|
||||
<div className="text-2xl font-semibold text-amber-300">
|
||||
${totalPrice.toFixed(2)}
|
||||
</div>
|
||||
<div className="text-xs text-zinc-500 mt-1">
|
||||
{selectedParts.length} / {CATEGORIES.length} categories filled
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="mt-4 border-t border-zinc-800 pt-3 space-y-2">
|
||||
{/* Primary actions now live under the total */}
|
||||
<div className="mt-2 flex flex-col items-stretch gap-2 sm:flex-row sm:flex-wrap">
|
||||
<Link
|
||||
href="/gunbuilder/build"
|
||||
className={`block w-full rounded-md border border-amber-400/60 bg-amber-400/10 px-3 py-2 text-sm font-medium text-amber-200 hover:bg-amber-400/15 text-center transition-colors ${
|
||||
className={`w-full sm:w-auto rounded-md border border-amber-400/60 bg-amber-400/10 px-3 py-2 text-sm font-medium text-amber-200 hover:bg-amber-400/15 text-center transition-colors ${
|
||||
selectedParts.length === 0
|
||||
? "opacity-40 cursor-not-allowed pointer-events-none"
|
||||
: ""
|
||||
@@ -444,6 +391,18 @@ export default function GunbuilderPage() {
|
||||
>
|
||||
Build Summary
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleShare}
|
||||
className={`w-full sm:w-auto rounded-md px-3 py-2 text-sm font-semibold transition-colors border ${
|
||||
selectedParts.length === 0
|
||||
? "bg-zinc-900/80 text-zinc-600 border-zinc-800 cursor-not-allowed"
|
||||
: "bg-zinc-900/80 text-zinc-200 border-zinc-700 hover:bg-zinc-800"
|
||||
}`}
|
||||
disabled={selectedParts.length === 0}
|
||||
>
|
||||
Copy Build Summary
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -453,20 +412,245 @@ export default function GunbuilderPage() {
|
||||
}
|
||||
}}
|
||||
disabled={selectedParts.length === 0}
|
||||
className={`w-full rounded-md border border-zinc-700 bg-zinc-900/50 px-3 py-2 text-sm font-medium text-zinc-300 hover:bg-zinc-800 hover:border-zinc-600 transition-colors ${
|
||||
selectedParts.length === 0
|
||||
? "opacity-40 cursor-not-allowed"
|
||||
: ""
|
||||
className={`w-full sm:w-auto rounded-md border border-zinc-700 bg-zinc-900/50 px-3 py-2 text-sm font-medium text-zinc-300 hover:bg-zinc-800 hover:border-zinc-600 transition-colors ${
|
||||
selectedParts.length === 0 ? "opacity-40 cursor-not-allowed" : ""
|
||||
}`}
|
||||
>
|
||||
Clear Build
|
||||
</button>
|
||||
<p className="mt-2 text-[0.7rem] text-zinc-500">
|
||||
View your build breakdown and share it with others.
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: Share Your Build */}
|
||||
<div className="flex flex-col gap-3 md:items-end md:text-right md:flex-1">
|
||||
{selectedParts.length > 0 && shareUrl && (
|
||||
<div className="w-full md:w-auto">
|
||||
<h3 className="text-[0.7rem] font-semibold uppercase tracking-[0.16em] text-zinc-400">
|
||||
Share Your Build
|
||||
</h3>
|
||||
<p className="mt-1 text-[0.7rem] text-zinc-500">
|
||||
Share this link to let others view your build or bookmark it to come
|
||||
back later.
|
||||
</p>
|
||||
<div className="mt-2 flex flex-col gap-2 md:flex-row md:items-center">
|
||||
<div className="flex-1">
|
||||
<div className="text-[0.65rem] uppercase tracking-[0.16em] text-zinc-500 mb-1">
|
||||
Shareable URL
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={shareUrl}
|
||||
className="w-full rounded-md border border-zinc-700 bg-zinc-950/80 px-2 py-1.5 text-[0.7rem] text-zinc-200 font-mono outline-none focus:ring-1 focus:ring-amber-400"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-2 md:flex-none md:mt-5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleNativeShare}
|
||||
className="flex-1 md:flex-none rounded-md bg-zinc-900/80 px-3 py-1.5 text-[0.75rem] font-semibold text-zinc-100 border border-zinc-700 hover:bg-zinc-800 hover:border-zinc-500 transition-colors"
|
||||
>
|
||||
Share
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyLink}
|
||||
className="flex-1 md:flex-none rounded-md bg-amber-400 px-3 py-1.5 text-[0.75rem] font-semibold text-black hover:bg-amber-300 transition-colors"
|
||||
>
|
||||
Copy Link
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shareStatus && (
|
||||
<p className="mt-1 text-[0.7rem] text-zinc-500">
|
||||
{shareStatus}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Layout */}
|
||||
<section className="rounded-lg border border-zinc-800 bg-zinc-950/60 p-3 md:p-4">
|
||||
<p className="mb-4 text-xs text-zinc-500">
|
||||
Work top-down through the major sections. Each row shows your
|
||||
current pick for that part type, with price and a direct buy
|
||||
link—or a quick way to choose a part if you haven't picked
|
||||
one yet.
|
||||
</p>
|
||||
|
||||
{loading && (
|
||||
<p className="text-sm text-zinc-500">
|
||||
Loading parts from backend…
|
||||
</p>
|
||||
)}
|
||||
{error && !loading && (
|
||||
<p className="text-sm text-red-400">
|
||||
{error} — check that the Ballistic API is running and CORS is
|
||||
configured.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!loading && !error && (
|
||||
<div className="space-y-6">
|
||||
{CATEGORY_GROUPS.map((group) => {
|
||||
const groupCategories = CATEGORIES.filter((c) =>
|
||||
group.categoryIds.includes(c.id as CategoryId),
|
||||
);
|
||||
|
||||
if (groupCategories.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={group.id} className="space-y-3">
|
||||
<div className="flex items-baseline justify-between gap-2">
|
||||
<div>
|
||||
<h2 className="text-sm font-semibold text-zinc-100">
|
||||
{group.label}
|
||||
</h2>
|
||||
{group.description && (
|
||||
<p className="text-xs text-zinc-500 mt-1 max-w-xl">
|
||||
{group.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto rounded-md border border-zinc-800 bg-zinc-950/80">
|
||||
<table className="min-w-full text-xs md:text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-zinc-800 bg-zinc-900/60">
|
||||
<th className="px-3 py-2 text-left font-semibold text-zinc-400">
|
||||
Component / Part Type
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left font-semibold text-zinc-400">
|
||||
Brand
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-semibold text-zinc-400">
|
||||
Price
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-semibold text-zinc-400">
|
||||
Sale Price
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left font-semibold text-zinc-400">
|
||||
Caliber
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right font-semibold text-zinc-400">
|
||||
Buy / Choose
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{groupCategories.map((category) => {
|
||||
const categoryParts =
|
||||
partsByCategory[category.id] ?? [];
|
||||
const selectedPartId = build[category.id];
|
||||
const selectedPart = categoryParts.find(
|
||||
(p) => p.id === selectedPartId,
|
||||
);
|
||||
const hasParts = categoryParts.length > 0;
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={category.id}
|
||||
className="border-t border-zinc-900 hover:bg-zinc-900/60 transition-colors"
|
||||
>
|
||||
{/* Component / Part Type */}
|
||||
<td className="px-3 py-2 align-top">
|
||||
<div className="font-medium text-zinc-100">
|
||||
{category.name}
|
||||
</div>
|
||||
{selectedPart ? (
|
||||
<div className="text-[0.7rem] text-zinc-500 line-clamp-1">
|
||||
{selectedPart.name}
|
||||
</div>
|
||||
) : hasParts ? (
|
||||
<div className="text-[0.7rem] text-zinc-600">
|
||||
No part selected
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-[0.7rem] text-zinc-600">
|
||||
No parts available yet
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
|
||||
{/* Brand */}
|
||||
<td className="px-3 py-2 align-top text-zinc-300">
|
||||
{selectedPart ? selectedPart.brand : "—"}
|
||||
</td>
|
||||
|
||||
{/* Price */}
|
||||
<td className="px-3 py-2 align-top text-right text-amber-300">
|
||||
{selectedPart
|
||||
? `$${selectedPart.price.toFixed(2)}`
|
||||
: "—"}
|
||||
</td>
|
||||
|
||||
{/* Sale Price (placeholder until we wire through sale/original) */}
|
||||
<td className="px-3 py-2 align-top text-right text-zinc-400">
|
||||
{/* TODO: wire in sale/original prices from backend */}
|
||||
{selectedPart ? "—" : "—"}
|
||||
</td>
|
||||
|
||||
{/* Caliber (placeholder for now) */}
|
||||
<td className="px-3 py-2 align-top text-zinc-400">
|
||||
{/* TODO: wire in caliber from ProductSummaryDto when available */}
|
||||
—
|
||||
</td>
|
||||
|
||||
{/* Buy / Choose */}
|
||||
<td className="px-3 py-2 align-top">
|
||||
<div className="flex justify-end gap-2">
|
||||
{selectedPart && selectedPart.url ? (
|
||||
<>
|
||||
<Link
|
||||
href={`/gunbuilder/${category.id}`}
|
||||
className="hidden md:inline-flex rounded-md border border-zinc-700 bg-zinc-900/70 px-2.5 py-1 text-[0.7rem] font-medium text-zinc-300 hover:bg-zinc-800 hover:border-zinc-600 transition-colors"
|
||||
>
|
||||
Change Part
|
||||
</Link>
|
||||
<a
|
||||
href={selectedPart.url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex rounded-md bg-amber-400 px-2.5 py-1 text-[0.7rem] font-semibold text-black hover:bg-amber-300 transition-colors"
|
||||
>
|
||||
Buy
|
||||
</a>
|
||||
</>
|
||||
) : hasParts ? (
|
||||
<Link
|
||||
href={`/gunbuilder/${category.id}`}
|
||||
className="inline-flex rounded-md border border-zinc-700 bg-zinc-900/70 px-2.5 py-1 text-[0.7rem] font-medium text-zinc-200 hover:bg-zinc-800 hover:border-zinc-600 transition-colors"
|
||||
onClick={() => {
|
||||
// If you want to pre-select something later, you can handle here.
|
||||
}}
|
||||
>
|
||||
Choose a Part
|
||||
</Link>
|
||||
) : (
|
||||
<span className="text-[0.7rem] text-zinc-600">
|
||||
—
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* What's New */}
|
||||
<section className="mt-8 rounded-lg border border-zinc-800 bg-zinc-950/80 p-3 md:p-4">
|
||||
|
||||
+3
-3
@@ -5,10 +5,10 @@ export default function HomePage() {
|
||||
<main className="min-h-screen bg-black text-zinc-50 flex items-center justify-center px-4">
|
||||
<div className="max-w-xl text-center">
|
||||
<p className="text-xs font-semibold tracking-[0.2em] uppercase text-zinc-500">
|
||||
Shadow Standard
|
||||
Shadow Standard Co.
|
||||
</p>
|
||||
<h1 className="mt-2 text-3xl md:text-4xl font-semibold tracking-tight">
|
||||
Gunbuilder Prototype
|
||||
The Build Bench
|
||||
</h1>
|
||||
<p className="mt-3 text-sm md:text-base text-zinc-400">
|
||||
This is a minimal Next.js + Tailwind prototype of the Gunbuilder
|
||||
@@ -20,7 +20,7 @@ export default function HomePage() {
|
||||
href="/gunbuilder"
|
||||
className="inline-flex items-center justify-center rounded-md border border-amber-400/70 bg-amber-400/10 px-4 py-2 text-sm font-medium text-amber-200 hover:bg-amber-400/15"
|
||||
>
|
||||
Open Gunbuilder
|
||||
Open The Build Bench
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user