This commit is contained in:
2025-12-01 16:19:18 -05:00
parent 309d164139
commit e4d25c3ea8
31 changed files with 271 additions and 13 deletions
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.
+2 -2
View File
@@ -2,6 +2,6 @@
"/gunbuilder/page": "app/gunbuilder/page.js",
"/gunbuilder/[categoryId]/page": "app/gunbuilder/[categoryId]/page.js",
"/gunbuilder/[categoryId]/[partId]/page": "app/gunbuilder/[categoryId]/[partId]/page.js",
"/gunbuilder/build/page": "app/gunbuilder/build/page.js",
"/gunbuilder/admin/category-mappings/page": "app/gunbuilder/admin/category-mappings/page.js"
"/gunbuilder/admin/category-mappings/page": "app/gunbuilder/admin/category-mappings/page.js",
"/gunbuilder/build/page": "app/gunbuilder/build/page.js"
}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -125,7 +125,7 @@
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("2b3feb8710102491")
/******/ __webpack_require__.h = () => ("4d1c41951abed137")
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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\"] = (\"a219314b11ef\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJhMjE5MzE0YjExZWZcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\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\"] = (\"52fdc5f75b38\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCI1MmZkYzVmNzViMzhcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
/***/ })
+1 -1
View File
@@ -192,7 +192,7 @@
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ !function() {
/******/ __webpack_require__.h = function() { return "94cf6984137bd852"; }
/******/ __webpack_require__.h = function() { return "99147c32c43e763f"; }
/******/ }();
/******/
/******/ /* webpack/runtime/global */
+19
View File
@@ -599,6 +599,9 @@ video {
.h-full {
height: 100%;
}
.h-3 {
height: 0.75rem;
}
.max-h-80 {
max-height: 20rem;
}
@@ -611,6 +614,9 @@ video {
.w-full {
width: 100%;
}
.w-3 {
width: 0.75rem;
}
.min-w-0 {
min-width: 0px;
}
@@ -675,6 +681,9 @@ video {
.gap-6 {
gap: 1.5rem;
}
.gap-1 {
gap: 0.25rem;
}
.space-y-1 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
@@ -714,6 +723,9 @@ video {
.rounded-md {
border-radius: 0.375rem;
}
.rounded-full {
border-radius: 9999px;
}
.border {
border-width: 1px;
}
@@ -830,6 +842,9 @@ video {
.bg-red-500\/10 {
background-color: rgb(239 68 68 / 0.1);
}
.bg-zinc-700\/40 {
background-color: rgb(63 63 70 / 0.4);
}
.object-contain {
-o-object-fit: contain;
object-fit: contain;
@@ -1059,6 +1074,10 @@ video {
--tw-text-opacity: 1;
color: rgb(254 202 202 / var(--tw-text-opacity));
}
.text-emerald-300 {
--tw-text-opacity: 1;
color: rgb(110 231 183 / var(--tw-text-opacity));
}
.underline {
text-decoration-line: underline;
}
@@ -0,0 +1 @@
{"c":["app/layout","app/gunbuilder/admin/category-mappings/page","webpack"],"r":[],"m":[]}
@@ -0,0 +1 @@
{"c":["app/layout","app/gunbuilder/admin/category-mappings/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
@@ -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\"] = (\"3a328a107f23\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCIzYTMyOGExMDdmMjNcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\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\"] = (\"e2a80d7b87cf\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJlMmE4MGQ3Yjg3Y2ZcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\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\"] = (\"52fdc5f75b38\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCI1MmZkYzVmNzViMzhcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
/***/ })
});
@@ -0,0 +1 @@
{"c":["app/layout","app/gunbuilder/admin/category-mappings/page","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 "ef2c3cea36ba80d6"; }
/******/ }();
/******/
/******/ }
);
@@ -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 "445131246b4eff72"; }
/******/ }();
/******/
/******/ }
);
@@ -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 "99147c32c43e763f"; }
/******/ }();
/******/
/******/ }
);
+3
View File
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
"use client";
import { useEffect, useState } from "react";
import { useEffect, useState, useMemo } from "react";
const API_BASE_URL =
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
@@ -50,6 +50,29 @@ export default function CategoryMappingsPage() {
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const [showOnlyUnmapped, setShowOnlyUnmapped] = useState(false);
const sortedMappings = useMemo(() => {
// Mapped rows first, then unmapped; within each group, alpha by rawCategory
return [...mappings].sort((a, b) => {
const aMapped = !!a.mappedPartRole;
const bMapped = !!b.mappedPartRole;
if (aMapped !== bMapped) {
return aMapped ? -1 : 1; // mapped first
}
return a.rawCategory.localeCompare(b.rawCategory);
});
}, [mappings]);
const visibleMappings = useMemo(
() =>
showOnlyUnmapped
? sortedMappings.filter((m) => !m.mappedPartRole)
: sortedMappings,
[sortedMappings, showOnlyUnmapped],
);
// Load merchants
useEffect(() => {
@@ -232,10 +255,22 @@ export default function CategoryMappingsPage() {
</p>
) : (
<>
<div className="mb-3 text-xs text-zinc-500">
Mappings for <span className="text-zinc-300">{selectedMerchantName}</span>.{" "}
Set a part role for each raw category. Unmapped categories will
be skipped by the importer.
<div className="mb-3 flex items-center justify-between gap-3 text-xs text-zinc-500">
<div>
Mappings for{" "}
<span className="text-zinc-300">{selectedMerchantName}</span>.{" "}
Set a part role for each raw category. Unmapped categories will
be skipped by the importer.
</div>
<label className="inline-flex items-center gap-1 text-[0.7rem] text-zinc-400">
<input
type="checkbox"
className="h-3 w-3 rounded border-zinc-600 bg-zinc-900"
checked={showOnlyUnmapped}
onChange={(e) => setShowOnlyUnmapped(e.target.checked)}
/>
<span>Show only unmapped</span>
</label>
</div>
<div className="overflow-x-auto">
@@ -244,10 +279,11 @@ export default function CategoryMappingsPage() {
<tr className="border-b border-zinc-800 text-xs uppercase tracking-[0.16em] text-zinc-500">
<th className="py-2 text-left pr-4">Raw Category</th>
<th className="py-2 text-left pr-4">Mapped Part Role</th>
<th className="py-2 text-left pr-4">Status</th>
</tr>
</thead>
<tbody>
{mappings.map((m) => (
{visibleMappings.map((m) => (
<tr
key={m.rawCategory}
className="border-b border-zinc-900 last:border-b-0"
@@ -273,6 +309,17 @@ export default function CategoryMappingsPage() {
))}
</select>
</td>
<td className="py-2 pr-4 align-top text-sm">
{m.mappedPartRole ? (
<span className="inline-flex items-center rounded-full bg-emerald-500/10 px-2 py-0.5 text-[0.7rem] font-medium text-emerald-300">
Mapped
</span>
) : (
<span className="inline-flex items-center rounded-full bg-zinc-700/40 px-2 py-0.5 text-[0.7rem] font-medium text-zinc-300">
Unmapped / Ignored
</span>
)}
</td>
</tr>
))}
</tbody>