feat: implemented CRUD funnctioality to contractors

This commit is contained in:
Тимур Городецкий
2026-07-27 14:49:43 +05:00
commit 02013a9bb5
68 changed files with 25385 additions and 0 deletions
@@ -0,0 +1,135 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var React = tslib_1.__importStar(require("react"));
var ContractorsList_module_scss_1 = tslib_1.__importDefault(require("./ContractorsList.module.scss"));
var CONTRACTOR_TYPES = ["Заказчик", "Поставщик"];
var CloseIcon = function () { return (React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "currentColor" },
React.createElement("path", { d: "M4.293 4.293a1 1 0 0 1 1.414 0L10 8.586l4.293-4.293a1 1 0 1 1 1.414 1.414L11.414 10l4.293 4.293a1 1 0 0 1-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L8.586 10 4.293 5.707a1 1 0 0 1 0-1.414z" }))); };
var ContractorModal = /** @class */ (function (_super) {
tslib_1.__extends(ContractorModal, _super);
function ContractorModal(props) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
var _this = _super.call(this, props) || this;
_this._handleKeyDown = function (e) {
if (e.key === "Escape" && !_this.state.saving)
_this.props.onClose();
};
_this._handleSave = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, fullName, name, contractorType, bin, kbe, oked, country, address, actualAddress, phone, e_1;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = this.state, fullName = _a.fullName, name = _a.name, contractorType = _a.contractorType, bin = _a.bin, kbe = _a.kbe, oked = _a.oked, country = _a.country, address = _a.address, actualAddress = _a.actualAddress, phone = _a.phone;
if (!name.trim() || !fullName.trim()) {
this.setState({ error: "Название и полное наименование обязательны для заполнения" });
return [2 /*return*/];
}
this.setState({ saving: true, error: null });
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.props.onSave({
fullName: fullName.trim(),
name: name.trim(),
contractorType: contractorType.trim() || null,
bin: bin.trim() || null,
kbe: kbe.trim() || null,
oked: oked.trim() || null,
country: country.trim() || null,
address: address.trim() || null,
actualAddress: actualAddress.trim() || null,
phone: phone.trim() || null,
})];
case 2:
_b.sent();
return [3 /*break*/, 4];
case 3:
e_1 = _b.sent();
this.setState({ saving: false, error: e_1.message });
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); };
var contractor = props.contractor;
_this.state = {
fullName: (_a = contractor === null || contractor === void 0 ? void 0 : contractor.fullName) !== null && _a !== void 0 ? _a : "",
name: (_b = contractor === null || contractor === void 0 ? void 0 : contractor.name) !== null && _b !== void 0 ? _b : "",
contractorType: (_c = contractor === null || contractor === void 0 ? void 0 : contractor.contractorType) !== null && _c !== void 0 ? _c : "",
bin: (_d = contractor === null || contractor === void 0 ? void 0 : contractor.bin) !== null && _d !== void 0 ? _d : "",
kbe: (_e = contractor === null || contractor === void 0 ? void 0 : contractor.kbe) !== null && _e !== void 0 ? _e : "",
oked: (_f = contractor === null || contractor === void 0 ? void 0 : contractor.oked) !== null && _f !== void 0 ? _f : "",
country: (_g = contractor === null || contractor === void 0 ? void 0 : contractor.country) !== null && _g !== void 0 ? _g : "",
address: (_h = contractor === null || contractor === void 0 ? void 0 : contractor.address) !== null && _h !== void 0 ? _h : "",
actualAddress: (_j = contractor === null || contractor === void 0 ? void 0 : contractor.actualAddress) !== null && _j !== void 0 ? _j : "",
phone: (_k = contractor === null || contractor === void 0 ? void 0 : contractor.phone) !== null && _k !== void 0 ? _k : "",
saving: false,
error: null,
};
return _this;
}
ContractorModal.prototype.componentDidMount = function () {
document.addEventListener("keydown", this._handleKeyDown);
};
ContractorModal.prototype.componentWillUnmount = function () {
document.removeEventListener("keydown", this._handleKeyDown);
};
ContractorModal.prototype.render = function () {
var _this = this;
var _a = this.props, contractor = _a.contractor, onClose = _a.onClose;
var _b = this.state, fullName = _b.fullName, name = _b.name, contractorType = _b.contractorType, bin = _b.bin, kbe = _b.kbe, oked = _b.oked, country = _b.country, address = _b.address, actualAddress = _b.actualAddress, phone = _b.phone, saving = _b.saving, error = _b.error;
var isEdit = !!contractor;
return (React.createElement("div", { className: ContractorsList_module_scss_1.default.modalOverlay },
React.createElement("div", { className: ContractorsList_module_scss_1.default.modal, onClick: function (e) { return e.stopPropagation(); }, role: "dialog", "aria-modal": "true" },
React.createElement("div", { className: ContractorsList_module_scss_1.default.modalHeader },
React.createElement("h3", { className: ContractorsList_module_scss_1.default.modalTitle }, isEdit ? "Редактировать контрагента" : "Новый контрагент"),
React.createElement("button", { className: ContractorsList_module_scss_1.default.modalCloseBtn, onClick: onClose, disabled: saving, title: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C" },
React.createElement(CloseIcon, null))),
React.createElement("div", { className: ContractorsList_module_scss_1.default.modalBody },
error && React.createElement("div", { className: ContractorsList_module_scss_1.default.errorMsg }, error),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel },
"\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 ",
React.createElement("span", { className: ContractorsList_module_scss_1.default.required }, "*")),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: name, onChange: function (e) { return _this.setState({ name: e.target.value, error: null }); }, placeholder: "\u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: Arx Minerals (\u0410\u0440\u043A\u0441 \u041C\u0438\u043D\u0435\u0440\u0430\u043B\u0441) \u0422\u041E\u041E", autoFocus: true })),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel },
"\u041F\u043E\u043B\u043D\u043E\u0435 \u043D\u0430\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D\u0438\u0435 ",
React.createElement("span", { className: ContractorsList_module_scss_1.default.required }, "*")),
React.createElement("textarea", { className: ContractorsList_module_scss_1.default.formTextarea, value: fullName, onChange: function (e) { return _this.setState({ fullName: e.target.value, error: null }); }, placeholder: '\u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0422\u041E\u041E "Arx Minerals (\u0410\u0440\u043A\u0441 \u041C\u0438\u043D\u0435\u0440\u0430\u043B\u0441)"', rows: 2 })),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGrid },
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u0411\u0418\u041D"),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: bin, onChange: function (e) { return _this.setState({ bin: e.target.value.replace(/\D/g, "").slice(0, 12) }); }, placeholder: "121140000288", inputMode: "numeric", maxLength: 12 })),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u0422\u0438\u043F \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u0430"),
React.createElement("select", { className: ContractorsList_module_scss_1.default.formInput, value: contractorType, onChange: function (e) { return _this.setState({ contractorType: e.target.value }); } },
React.createElement("option", { value: "" }, "\u2014 \u041D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E \u2014"),
CONTRACTOR_TYPES.map(function (t) { return React.createElement("option", { key: t, value: t }, t); }))),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u041A\u0411\u0415"),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: kbe, onChange: function (e) { return _this.setState({ kbe: e.target.value }); } })),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u041E\u041A\u042D\u0414"),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: oked, onChange: function (e) { return _this.setState({ oked: e.target.value }); } })),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u0421\u0442\u0440\u0430\u043D\u0430"),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: country, onChange: function (e) { return _this.setState({ country: e.target.value }); } })),
React.createElement("div", { className: ContractorsList_module_scss_1.default.formGroup },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u0422\u0435\u043B\u0435\u0444\u043E\u043D"),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: phone, onChange: function (e) { return _this.setState({ phone: e.target.value }); } })),
React.createElement("div", { className: "".concat(ContractorsList_module_scss_1.default.formGroup, " ").concat(ContractorsList_module_scss_1.default.formGroupFull) },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u0410\u0434\u0440\u0435\u0441"),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: address, onChange: function (e) { return _this.setState({ address: e.target.value }); } })),
React.createElement("div", { className: "".concat(ContractorsList_module_scss_1.default.formGroup, " ").concat(ContractorsList_module_scss_1.default.formGroupFull) },
React.createElement("label", { className: ContractorsList_module_scss_1.default.formLabel }, "\u0424\u0430\u043A\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0430\u0434\u0440\u0435\u0441"),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.formInput, value: actualAddress, onChange: function (e) { return _this.setState({ actualAddress: e.target.value }); } })))),
React.createElement("div", { className: ContractorsList_module_scss_1.default.modalFooter },
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnSecondary, onClick: onClose, disabled: saving }, "\u041E\u0442\u043C\u0435\u043D\u0430"),
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnPrimary, onClick: this._handleSave, disabled: saving }, saving ? "Сохранение..." : isEdit ? "Сохранить" : "Создать")))));
};
return ContractorModal;
}(React.Component));
exports.default = ContractorModal;
//# sourceMappingURL=ContractorModal.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,240 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var React = tslib_1.__importStar(require("react"));
var ContractorsList_module_scss_1 = tslib_1.__importDefault(require("./ContractorsList.module.scss"));
var sp_http_1 = require("@microsoft/sp-http");
var ContractorModal_1 = tslib_1.__importDefault(require("./ContractorModal"));
var DeleteModal_1 = tslib_1.__importDefault(require("./DeleteModal"));
var SearchIcon = function () { return (React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "currentColor" },
React.createElement("path", { d: "M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12z" }))); };
var PlusIcon = function () { return (React.createElement("svg", { width: "14", height: "14", viewBox: "0 0 20 20", fill: "currentColor" },
React.createElement("path", { d: "M10 3a1 1 0 0 1 1 1v5h5a1 1 0 1 1 0 2h-5v5a1 1 0 1 1-2 0v-5H4a1 1 0 1 1 0-2h5V4a1 1 0 0 1 1-1z" }))); };
var ContractorIcon = function () { return (React.createElement("svg", { width: "48", height: "48", viewBox: "0 0 48 48", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
React.createElement("rect", { x: "8", y: "18", width: "32", height: "24", rx: "1" }),
React.createElement("path", { d: "M4 20L24 6L44 20" }),
React.createElement("rect", { x: "19", y: "30", width: "10", height: "12", rx: "1" }),
React.createElement("rect", { x: "12", y: "24", width: "6", height: "6", rx: "1" }),
React.createElement("rect", { x: "30", y: "24", width: "6", height: "6", rx: "1" }))); };
var EditIcon = function () { return (React.createElement("svg", { width: "13", height: "13", viewBox: "0 0 20 20", fill: "currentColor" },
React.createElement("path", { d: "M17.04 2.96a3.28 3.28 0 0 0-4.64 0L3.29 12.07a1 1 0 0 0-.27.51l-1 5a1 1 0 0 0 1.18 1.18l5-1a1 1 0 0 0 .51-.27l9.11-9.11a3.28 3.28 0 0 0 0-4.64zm-3.23 1.41a1.28 1.28 0 0 1 1.82 1.82l-1.28 1.28-1.82-1.82 1.28-1.28zM4.9 13.17l7.18-7.18 1.82 1.82-7.18 7.18-3.18.64.64-3.18z" }))); };
var TrashIcon = function () { return (React.createElement("svg", { width: "13", height: "13", viewBox: "0 0 20 20", fill: "currentColor" },
React.createElement("path", { d: "M8.5 3a1 1 0 0 0-1 1H5a1 1 0 0 0 0 2h10a1 1 0 1 0 0-2h-2.5a1 1 0 0 0-1-1h-3zM5 7v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V7H5zm3 2a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0v-5.5A.75.75 0 0 1 8 9zm4 0a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0v-5.5A.75.75 0 0 1 12 9z" }))); };
var PAGE_SIZE = 20;
var ContractorsList = /** @class */ (function (_super) {
tslib_1.__extends(ContractorsList, _super);
function ContractorsList(props) {
var _this = _super.call(this, props) || this;
_this._searchTimeout = null;
_this._handleSearchInput = function (e) {
var searchInput = e.target.value;
_this.setState({ searchInput: searchInput, page: 1 });
if (_this._searchTimeout)
clearTimeout(_this._searchTimeout);
_this._searchTimeout = setTimeout(function () {
void _this._loadContractors(1, searchInput);
}, 400);
};
_this._handlePageChange = function (newPage) {
_this.setState({ page: newPage });
void _this._loadContractors(newPage, _this.state.searchInput);
};
_this._handleModalSave = function (data) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, httpClient, apiBaseUrl, editingContractor, response, _b;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = this.props, httpClient = _a.httpClient, apiBaseUrl = _a.apiBaseUrl;
editingContractor = this.state.editingContractor;
if (!editingContractor) return [3 /*break*/, 2];
return [4 /*yield*/, httpClient.fetch("".concat(apiBaseUrl, "/Contractors/").concat(editingContractor.id), sp_http_1.AadHttpClient.configurations.v1, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
})];
case 1:
_b = _c.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, httpClient.fetch("".concat(apiBaseUrl, "/Contractors"), sp_http_1.AadHttpClient.configurations.v1, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
})];
case 3:
_b = _c.sent();
_c.label = 4;
case 4:
response = _b;
if (!response.ok)
throw new Error("\u041E\u0448\u0438\u0431\u043A\u0430 ".concat(response.status, ": ").concat(response.statusText));
this.setState({ showModal: false, editingContractor: null, page: 1 });
return [4 /*yield*/, this._loadContractors(1, this.state.searchInput)];
case 5:
_c.sent();
return [2 /*return*/];
}
});
}); };
_this._handleDeleteConfirm = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, httpClient, apiBaseUrl, _b, deletingContractor, page, searchInput, response;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = this.props, httpClient = _a.httpClient, apiBaseUrl = _a.apiBaseUrl;
_b = this.state, deletingContractor = _b.deletingContractor, page = _b.page, searchInput = _b.searchInput;
if (!deletingContractor)
return [2 /*return*/];
return [4 /*yield*/, httpClient.fetch("".concat(apiBaseUrl, "/Contractors/").concat(deletingContractor.id), sp_http_1.AadHttpClient.configurations.v1, { method: "DELETE" })];
case 1:
response = _c.sent();
if (!response.ok)
throw new Error("\u041E\u0448\u0438\u0431\u043A\u0430 ".concat(response.status, ": ").concat(response.statusText));
this.setState({ deletingContractor: null });
return [4 /*yield*/, this._loadContractors(page, searchInput)];
case 2:
_c.sent();
return [2 /*return*/];
}
});
}); };
_this.state = {
contractors: [],
totalCount: 0,
totalPages: 1,
page: 1,
loading: true,
error: null,
searchInput: "",
showModal: false,
editingContractor: null,
deletingContractor: null,
};
return _this;
}
ContractorsList.prototype.componentDidMount = function () {
void this._loadContractors(1, "");
};
ContractorsList.prototype._loadContractors = function (page, name) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, httpClient, apiBaseUrl, nameParam, url, response, data, e_1;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = this.props, httpClient = _a.httpClient, apiBaseUrl = _a.apiBaseUrl;
this.setState({ loading: true, error: null });
_b.label = 1;
case 1:
_b.trys.push([1, 4, , 5]);
nameParam = name ? "&name=".concat(encodeURIComponent(name)) : "";
url = "".concat(apiBaseUrl, "/Contractors?page=").concat(page, "&pageSize=").concat(PAGE_SIZE).concat(nameParam);
return [4 /*yield*/, httpClient.get(url, sp_http_1.AadHttpClient.configurations.v1)];
case 2:
response = _b.sent();
if (!response.ok)
throw new Error("\u041E\u0448\u0438\u0431\u043A\u0430 ".concat(response.status, ": ").concat(response.statusText));
return [4 /*yield*/, response.json()];
case 3:
data = _b.sent();
this.setState({
contractors: data.items,
totalCount: data.totalCount,
totalPages: data.totalPages,
loading: false,
});
return [3 /*break*/, 5];
case 4:
e_1 = _b.sent();
this.setState({ loading: false, error: e_1.message });
return [3 /*break*/, 5];
case 5: return [2 /*return*/];
}
});
});
};
ContractorsList.prototype._renderPageNumbers = function (currentPage, totalPages) {
var _this = this;
var btn = function (p) { return (React.createElement("button", { key: p, className: "".concat(ContractorsList_module_scss_1.default.pageBtn, " ").concat(p === currentPage ? ContractorsList_module_scss_1.default.pageBtnActive : ""), onClick: function () { return _this._handlePageChange(p); } }, p)); };
if (totalPages <= 7) {
return Array.from({ length: totalPages }, function (_, i) { return btn(i + 1); });
}
var result = [btn(1)];
if (currentPage > 3)
result.push(React.createElement("span", { key: "e1", className: ContractorsList_module_scss_1.default.pageEllipsis }, "\u2026"));
var start = Math.max(2, currentPage - 1);
var end = Math.min(totalPages - 1, currentPage + 1);
for (var i = start; i <= end; i++)
result.push(btn(i));
if (currentPage < totalPages - 2)
result.push(React.createElement("span", { key: "e2", className: ContractorsList_module_scss_1.default.pageEllipsis }, "\u2026"));
result.push(btn(totalPages));
return result;
};
ContractorsList.prototype.render = function () {
var _this = this;
var isDarkTheme = this.props.isDarkTheme;
var _a = this.state, contractors = _a.contractors, totalCount = _a.totalCount, totalPages = _a.totalPages, page = _a.page, loading = _a.loading, error = _a.error, searchInput = _a.searchInput, showModal = _a.showModal, editingContractor = _a.editingContractor, deletingContractor = _a.deletingContractor;
var currentPage = Math.min(page, totalPages);
var from = totalCount === 0 ? 0 : (currentPage - 1) * PAGE_SIZE + 1;
var to = Math.min(currentPage * PAGE_SIZE, totalCount);
return (React.createElement("div", { className: "".concat(ContractorsList_module_scss_1.default.root, " ").concat(isDarkTheme ? ContractorsList_module_scss_1.default.dark : "") },
React.createElement("div", { className: ContractorsList_module_scss_1.default.toolbar },
React.createElement("h2", { className: ContractorsList_module_scss_1.default.pageTitle }, "\u041A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u044B"),
React.createElement("div", { className: ContractorsList_module_scss_1.default.toolbarRight },
React.createElement("div", { className: ContractorsList_module_scss_1.default.searchBox },
React.createElement("span", { className: ContractorsList_module_scss_1.default.searchIcon },
React.createElement(SearchIcon, null)),
React.createElement("input", { type: "text", className: ContractorsList_module_scss_1.default.searchInput, placeholder: "\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u044E...", value: searchInput, onChange: this._handleSearchInput })),
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnPrimary, onClick: function () { return _this.setState({ showModal: true, editingContractor: null }); } },
React.createElement(PlusIcon, null),
React.createElement("span", null, "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u0430")))),
error && React.createElement("div", { className: ContractorsList_module_scss_1.default.errorMsg }, error),
loading && (React.createElement("div", { className: ContractorsList_module_scss_1.default.loaderWrap },
React.createElement("div", { className: ContractorsList_module_scss_1.default.spinner }),
React.createElement("span", null, "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430..."))),
!loading && !error && totalCount === 0 && (React.createElement("div", { className: ContractorsList_module_scss_1.default.emptyState },
React.createElement("div", { className: ContractorsList_module_scss_1.default.emptyIcon },
React.createElement(ContractorIcon, null)),
React.createElement("p", { className: ContractorsList_module_scss_1.default.emptyTitle }, searchInput ? "Ничего не найдено" : "Контрагентов пока нет"),
!searchInput && (React.createElement(React.Fragment, null,
React.createElement("p", { className: ContractorsList_module_scss_1.default.emptyText }, "\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u0430, \u043D\u0430\u0436\u0430\u0432 \u043A\u043D\u043E\u043F\u043A\u0443 \u043D\u0438\u0436\u0435"),
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnPrimary, onClick: function () { return _this.setState({ showModal: true, editingContractor: null }); } },
React.createElement(PlusIcon, null),
React.createElement("span", null, "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u0430")))))),
!loading && totalCount > 0 && (React.createElement("div", { className: ContractorsList_module_scss_1.default.tableWrapper },
React.createElement("table", { className: ContractorsList_module_scss_1.default.table },
React.createElement("thead", { className: ContractorsList_module_scss_1.default.tableHead },
React.createElement("tr", null,
React.createElement("th", null, "\u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435"),
React.createElement("th", { className: ContractorsList_module_scss_1.default.binCol }, "\u0411\u0418\u041D"),
React.createElement("th", { className: ContractorsList_module_scss_1.default.tableActionsCol }, "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F"))),
React.createElement("tbody", null, contractors.map(function (c) { return (React.createElement("tr", { key: c.id, className: ContractorsList_module_scss_1.default.tableRow },
React.createElement("td", null,
React.createElement("div", { className: ContractorsList_module_scss_1.default.contractorName }, c.name),
React.createElement("div", { className: ContractorsList_module_scss_1.default.contractorFullName }, c.fullName)),
React.createElement("td", null, c.bin
? React.createElement("span", { className: ContractorsList_module_scss_1.default.binValue }, c.bin)
: React.createElement("span", { className: ContractorsList_module_scss_1.default.binEmpty }, "\u2014")),
React.createElement("td", null,
React.createElement("div", { className: ContractorsList_module_scss_1.default.tableActions },
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnEdit, title: "\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C", onClick: function () { return _this.setState({ showModal: true, editingContractor: c }); } },
React.createElement(EditIcon, null)),
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnDeleteCard, title: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C", onClick: function () { return _this.setState({ deletingContractor: c }); } },
React.createElement(TrashIcon, null)))))); }))))),
!loading && totalCount > 0 && (React.createElement("div", { className: ContractorsList_module_scss_1.default.pagination },
React.createElement("span", { className: ContractorsList_module_scss_1.default.paginationInfo },
from,
"\u2013",
to,
" \u0438\u0437 ",
totalCount),
totalPages > 1 && (React.createElement("div", { className: ContractorsList_module_scss_1.default.paginationControls },
React.createElement("button", { className: ContractorsList_module_scss_1.default.pageBtn, disabled: currentPage === 1, onClick: function () { return _this._handlePageChange(currentPage - 1); } }, "\u2039"),
this._renderPageNumbers(currentPage, totalPages),
React.createElement("button", { className: ContractorsList_module_scss_1.default.pageBtn, disabled: currentPage === totalPages, onClick: function () { return _this._handlePageChange(currentPage + 1); } }, "\u203A"))))),
showModal && (React.createElement(ContractorModal_1.default, { contractor: editingContractor, onSave: this._handleModalSave, onClose: function () { return _this.setState({ showModal: false, editingContractor: null }); } })),
deletingContractor && (React.createElement(DeleteModal_1.default, { contractorName: deletingContractor.name, onConfirm: this._handleDeleteConfirm, onClose: function () { return _this.setState({ deletingContractor: null }); } }))));
};
return ContractorsList;
}(React.Component));
exports.default = ContractorsList;
//# sourceMappingURL=ContractorsList.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,668 @@
:root {
--navy-950: #0A1B30;
--navy-900: #0F2440;
--navy-800: #1B3A5C;
--navy-700: #244B73;
--navy-600: #2D5C8A;
--navy-100: #E8EEF4;
--navy-50: #F4F7FA;
--gold-600: #B8881A;
--gold-500: #D4A01E;
--gold-400: #E5B635;
--gold-300: #F0CC5E;
--gold-100: #FDF5E0;
--gold-50: #FEFBF0;
--gray-900: #111827;
--gray-700: #374151;
--gray-500: #6B7280;
--gray-400: #9CA3AF;
--gray-300: #D1D5DB;
--gray-200: #E5E7EB;
--gray-100: #F3F4F6;
--gray-50: #F9FAFB;
--red-500: #DC2626;
--red-50: #FEF2F2;
--green-600: #15803D;
--green-50: #F0FDF4;
--blue-600: #2563EB;
--blue-50: #EFF6FF;
--amber-700: #854D0E;
--sh-1: 0 1px 2px rgba(15, 36, 64, 0.04), 0 1px 3px rgba(15, 36, 64, 0.06);
--sh-2: 0 4px 16px rgba(15, 36, 64, 0.07);
--sh-3: 0 12px 32px rgba(15, 36, 64, 0.1);
--sh-gold: 0 4px 20px rgba(212, 160, 30, 0.18);
--sh-modal: 0 24px 70px rgba(10, 27, 48, 0.25);
--r-sm: 6px;
--r-md: 10px;
--r-lg: 14px;
}
.root {
font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
font-size: 14px;
color: #111827;
background: linear-gradient(180deg, #f6f8fa 0%, #eef2f6 100%);
min-height: 200px;
padding: 0 0 32px;
}
.root.dark {
color: #f3f2f1;
background: #1b1a19;
}
.toolbar {
background: linear-gradient(135deg, #0A1B30 0%, #1B3A5C 85%);
border-radius: 14px;
padding: 26px 30px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin: 0 0 24px;
flex-wrap: wrap;
}
.toolbar::before {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 2px;
background: linear-gradient(90deg, #D4A01E, transparent 60%);
}
.pageTitle {
font-family: "Lora", Georgia, serif;
font-size: 24px;
font-weight: 600;
color: #fff;
margin: 0;
letter-spacing: 0.2px;
position: relative;
z-index: 1;
}
.toolbarRight {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
position: relative;
z-index: 1;
}
.searchBox {
display: flex;
align-items: center;
gap: 6px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 6px;
padding: 0 12px;
height: 36px;
min-width: 240px;
transition: 0.18s;
backdrop-filter: blur(4px);
}
.searchBox:focus-within {
border-color: #E5B635;
background: rgba(255, 255, 255, 0.14);
}
.searchIcon {
color: rgba(255, 255, 255, 0.4);
display: flex;
align-items: center;
flex-shrink: 0;
}
.searchInput {
border: none;
outline: none;
background: transparent;
font-size: 13px;
color: #fff;
width: 100%;
font-family: inherit;
}
.searchInput::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.errorMsg {
margin: 0 20px 16px;
padding: 12px 16px;
background: #FEF2F2;
border: 1px solid #fecaca;
border-radius: 10px;
color: #DC2626;
font-size: 12.5px;
line-height: 1.5;
}
.loaderWrap {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 48px 24px;
color: #6B7280;
font-size: 13px;
}
.spinner {
width: 18px;
height: 18px;
border: 2px solid #E5E7EB;
border-top-color: #D4A01E;
border-radius: 50%;
animation: spin 0.7s linear infinite;
flex-shrink: 0;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.emptyState {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 64px 24px;
text-align: center;
gap: 8px;
}
.emptyIcon {
color: #D1D5DB;
margin-bottom: 8px;
}
.emptyTitle {
font-family: "Lora", Georgia, serif;
font-size: 18px;
font-weight: 600;
color: #6B7280;
margin: 0;
}
.emptyText {
font-size: 13px;
color: #9CA3AF;
margin: 0;
}
.tableWrapper {
padding: 0 20px 20px;
overflow-x: auto;
}
.table {
width: 100%;
border-collapse: collapse;
font-size: 13.5px;
background: #fff;
border-radius: 14px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(15, 36, 64, 0.04), 0 1px 3px rgba(15, 36, 64, 0.06);
border: 1px solid #E5E7EB;
}
.tableHead th {
text-align: left;
padding: 13px 18px;
font-size: 10.5px;
text-transform: uppercase;
letter-spacing: 1.2px;
color: #9CA3AF;
font-weight: 600;
background: #F9FAFB;
border-bottom: 1px solid #E5E7EB;
white-space: nowrap;
}
.binCol {
width: 160px;
}
.tableRow {
border-bottom: 1px solid #F3F4F6;
transition: 0.12s;
}
.tableRow:last-child {
border-bottom: 0;
}
.tableRow:hover {
background: #FEFBF0;
}
.tableRow td {
padding: 15px 18px;
color: #374151;
vertical-align: middle;
}
.contractorName {
font-size: 14px;
font-weight: 500;
color: #0F2440;
letter-spacing: 0.1px;
}
.contractorFullName {
font-size: 12px;
color: #9CA3AF;
margin-top: 2px;
}
.binValue {
font-variant-numeric: tabular-nums;
color: #374151;
}
.binEmpty {
color: #9CA3AF;
}
.pagination {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 8px;
padding: 12px 20px 0;
}
.paginationInfo {
font-size: 12px;
color: #9CA3AF;
letter-spacing: 0.3px;
}
.paginationControls {
display: flex;
align-items: center;
gap: 4px;
}
.pageBtn {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 30px;
height: 30px;
padding: 0 6px;
background: #fff;
color: #6B7280;
border: 1px solid #D1D5DB;
border-radius: 6px;
font-size: 13px;
font-family: inherit;
cursor: pointer;
transition: 0.15s;
}
.pageBtn:hover:not(:disabled) {
border-color: #D4A01E;
color: #B8881A;
}
.pageBtn:disabled {
opacity: 0.35;
pointer-events: none;
}
.pageBtnActive {
background: #0F2440;
color: #F0CC5E;
border-color: #0F2440;
font-weight: 600;
}
.pageBtnActive:hover {
background: #1B3A5C;
border-color: #1B3A5C;
color: #F0CC5E;
}
.pageEllipsis {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 30px;
height: 30px;
color: #9CA3AF;
font-size: 13px;
user-select: none;
}
.btnPrimary {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 9px 18px;
background: #1B3A5C;
color: #fff;
border: 1px solid #1B3A5C;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
cursor: pointer;
white-space: nowrap;
letter-spacing: 0.15px;
transition: 0.18s;
flex-shrink: 0;
}
.btnPrimary:hover:not(:disabled) {
background: #244B73;
box-shadow: 0 4px 16px rgba(15, 36, 64, 0.07);
}
.btnPrimary:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btnSecondary {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 9px 18px;
background: #fff;
color: #1B3A5C;
border: 1px solid #D1D5DB;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
cursor: pointer;
transition: 0.18s;
white-space: nowrap;
}
.btnSecondary:hover:not(:disabled) {
border-color: #D4A01E;
color: #0F2440;
}
.btnSecondary:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btnDanger {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 9px 18px;
background: #fff;
color: #DC2626;
border: 1px solid #D1D5DB;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
cursor: pointer;
transition: 0.18s;
}
.btnDanger:hover:not(:disabled) {
border-color: #DC2626;
background: #FEF2F2;
}
.btnDanger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btnEdit {
display: inline-flex;
align-items: center;
justify-content: center;
width: 33px;
height: 33px;
padding: 0;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
color: #9CA3AF;
cursor: pointer;
transition: 0.18s;
}
.btnEdit:hover {
color: #1B3A5C;
background: #F4F7FA;
border-color: #E5E7EB;
}
.btnDeleteCard {
display: inline-flex;
align-items: center;
justify-content: center;
width: 33px;
height: 33px;
padding: 0;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
color: #9CA3AF;
cursor: pointer;
transition: 0.18s;
}
.btnDeleteCard:hover {
color: #DC2626;
background: #FEF2F2;
border-color: #DC2626;
}
.tableActionsCol {
width: 100px;
}
.tableActions {
display: flex;
gap: 4px;
align-items: center;
justify-content: flex-end;
}
.modalOverlay {
position: fixed;
inset: 0;
background: rgba(10, 27, 48, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
padding: 16px;
box-sizing: border-box;
backdrop-filter: blur(6px);
}
.modal {
background: #fff;
border-radius: 14px;
box-shadow: 0 24px 70px rgba(10, 27, 48, 0.25);
width: 100%;
max-width: 640px;
max-height: 90vh;
display: flex;
flex-direction: column;
overflow: hidden;
border-top: 3px solid #D4A01E;
}
.modalSm {
max-width: 420px;
}
.modalHeader {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 24px 16px;
border-bottom: 1px solid #E5E7EB;
flex-shrink: 0;
}
.modalTitle {
font-family: "Lora", Georgia, serif;
font-size: 22px;
font-weight: 600;
color: #0F2440;
margin: 0;
letter-spacing: 0.2px;
}
.modalTitleDanger {
display: flex;
align-items: center;
gap: 8px;
font-family: "Lora", Georgia, serif;
font-size: 22px;
font-weight: 600;
color: #DC2626;
margin: 0;
}
.warningIcon {
color: #DC2626;
display: flex;
align-items: center;
}
.modalCloseBtn {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
color: #9CA3AF;
cursor: pointer;
flex-shrink: 0;
transition: 0.18s;
}
.modalCloseBtn:hover:not(:disabled) {
background: #F3F4F6;
color: #374151;
border-color: #E5E7EB;
}
.modalCloseBtn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.modalBody {
padding: 24px;
overflow-y: auto;
flex: 1;
display: flex;
flex-direction: column;
gap: 16px;
}
.modalFooter {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 16px 24px;
border-top: 1px solid #E5E7EB;
flex-shrink: 0;
}
.formGrid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px 20px;
}
@media (max-width: 480px) {
.formGrid {
grid-template-columns: 1fr;
}
}
.formGroup {
display: flex;
flex-direction: column;
gap: 6px;
}
.formGroupFull {
grid-column: 1/-1;
}
.formLabel {
font-size: 12px;
font-weight: 500;
color: #374151;
letter-spacing: 0.2px;
}
.required {
color: #B8881A;
}
.formInput {
height: 38px;
padding: 0 13px;
border: 1px solid #D1D5DB;
border-radius: 6px;
font-size: 13.5px;
font-family: inherit;
color: #111827;
background: #fff;
transition: 0.18s;
box-sizing: border-box;
width: 100%;
}
.formInput:focus {
outline: 0;
border-color: #D4A01E;
box-shadow: 0 0 0 3px rgba(212, 160, 30, 0.12);
}
.formInput:disabled {
background: #F9FAFB;
color: #9CA3AF;
cursor: not-allowed;
}
.formInput::placeholder {
color: #9CA3AF;
}
.formTextarea {
padding: 11px 13px;
border: 1px solid #D1D5DB;
border-radius: 6px;
font-size: 13.5px;
font-family: inherit;
color: #111827;
background: #fff;
resize: vertical;
transition: 0.18s;
width: 100%;
box-sizing: border-box;
line-height: 1.5;
min-height: 60px;
}
.formTextarea:focus {
outline: 0;
border-color: #D4A01E;
box-shadow: 0 0 0 3px rgba(212, 160, 30, 0.12);
}
.formTextarea::placeholder {
color: #9CA3AF;
}
.deleteText {
font-size: 14px;
color: #374151;
margin: 0;
line-height: 1.6;
}
.deleteHint {
font-size: 12px;
color: #9CA3AF;
margin: 8px 0 0;
line-height: 1.6;
}
@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var React = tslib_1.__importStar(require("react"));
var ContractorsList_module_scss_1 = tslib_1.__importDefault(require("./ContractorsList.module.scss"));
var WarningIcon = function () { return (React.createElement("svg", { width: "24", height: "24", viewBox: "0 0 20 20", fill: "currentColor" },
React.createElement("path", { fillRule: "evenodd", d: "M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 6a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 6zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2z", clipRule: "evenodd" }))); };
var DeleteModal = /** @class */ (function (_super) {
tslib_1.__extends(DeleteModal, _super);
function DeleteModal(props) {
var _this = _super.call(this, props) || this;
_this._handleKeyDown = function (e) {
if (e.key === "Escape" && !_this.state.deleting)
_this.props.onClose();
if (e.key === "Enter" && !_this.state.deleting)
void _this._handleConfirm();
};
_this._handleConfirm = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var e_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
this.setState({ deleting: true, error: null });
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.props.onConfirm()];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
this.setState({ deleting: false, error: e_1.message });
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
}); };
_this.state = { deleting: false, error: null };
return _this;
}
DeleteModal.prototype.componentDidMount = function () {
document.addEventListener("keydown", this._handleKeyDown);
};
DeleteModal.prototype.componentWillUnmount = function () {
document.removeEventListener("keydown", this._handleKeyDown);
};
DeleteModal.prototype.render = function () {
var _a = this.props, contractorName = _a.contractorName, onClose = _a.onClose;
var _b = this.state, deleting = _b.deleting, error = _b.error;
return (React.createElement("div", { className: ContractorsList_module_scss_1.default.modalOverlay, onClick: !deleting ? onClose : undefined },
React.createElement("div", { className: "".concat(ContractorsList_module_scss_1.default.modal, " ").concat(ContractorsList_module_scss_1.default.modalSm), onClick: function (e) { return e.stopPropagation(); }, role: "dialog", "aria-modal": "true" },
React.createElement("div", { className: ContractorsList_module_scss_1.default.modalHeader },
React.createElement("div", { className: ContractorsList_module_scss_1.default.modalTitleDanger },
React.createElement("span", { className: ContractorsList_module_scss_1.default.warningIcon },
React.createElement(WarningIcon, null)),
"\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u0430")),
React.createElement("div", { className: ContractorsList_module_scss_1.default.modalBody },
error && React.createElement("div", { className: ContractorsList_module_scss_1.default.errorMsg }, error),
React.createElement("p", { className: ContractorsList_module_scss_1.default.deleteText },
"\u0412\u044B \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u0445\u043E\u0442\u0438\u0442\u0435 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442\u0430",
' ',
React.createElement("strong", null,
"\u00AB",
contractorName,
"\u00BB"),
"?"),
React.createElement("p", { className: ContractorsList_module_scss_1.default.deleteHint }, "\u041A\u043E\u043D\u0442\u0440\u0430\u0433\u0435\u043D\u0442 \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0451\u043D \u0438\u0437 \u0441\u043F\u0440\u0430\u0432\u043E\u0447\u043D\u0438\u043A\u0430.")),
React.createElement("div", { className: ContractorsList_module_scss_1.default.modalFooter },
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnSecondary, onClick: onClose, disabled: deleting }, "\u041E\u0442\u043C\u0435\u043D\u0430"),
React.createElement("button", { className: ContractorsList_module_scss_1.default.btnDanger, onClick: this._handleConfirm, disabled: deleting }, deleting ? "Удаление..." : "Удалить")))));
};
return DeleteModal;
}(React.Component));
exports.default = DeleteModal;
//# sourceMappingURL=DeleteModal.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=IContractorsListProps.js.map
@@ -0,0 +1 @@
{"version":3,"file":"IContractorsListProps.js","sourceRoot":"","sources":["../../../../src/webparts/contractorsList/components/IContractorsListProps.ts"],"names":[],"mappings":"","sourcesContent":["import { AadHttpClient } from '@microsoft/sp-http';\n\nexport interface IContractorsListProps {\n isDarkTheme: boolean;\n httpClient: AadHttpClient;\n apiBaseUrl: string;\n}\n"]}
@@ -0,0 +1,38 @@
:root {
--navy-950: #0A1B30;
--navy-900: #0F2440;
--navy-800: #1B3A5C;
--navy-700: #244B73;
--navy-600: #2D5C8A;
--navy-100: #E8EEF4;
--navy-50: #F4F7FA;
--gold-600: #B8881A;
--gold-500: #D4A01E;
--gold-400: #E5B635;
--gold-300: #F0CC5E;
--gold-100: #FDF5E0;
--gold-50: #FEFBF0;
--gray-900: #111827;
--gray-700: #374151;
--gray-500: #6B7280;
--gray-400: #9CA3AF;
--gray-300: #D1D5DB;
--gray-200: #E5E7EB;
--gray-100: #F3F4F6;
--gray-50: #F9FAFB;
--red-500: #DC2626;
--red-50: #FEF2F2;
--green-600: #15803D;
--green-50: #F0FDF4;
--blue-600: #2563EB;
--blue-50: #EFF6FF;
--amber-700: #854D0E;
--sh-1: 0 1px 2px rgba(15, 36, 64, 0.04), 0 1px 3px rgba(15, 36, 64, 0.06);
--sh-2: 0 4px 16px rgba(15, 36, 64, 0.07);
--sh-3: 0 12px 32px rgba(15, 36, 64, 0.1);
--sh-gold: 0 4px 20px rgba(212, 160, 30, 0.18);
--sh-modal: 0 24px 70px rgba(10, 27, 48, 0.25);
--r-sm: 6px;
--r-md: 10px;
--r-lg: 14px;
}