first commit

This commit is contained in:
Admin 2025-08-21 15:37:34 +07:00
parent a31a77d934
commit 276abd49f8
25 changed files with 474 additions and 158 deletions

View File

@ -2,6 +2,5 @@ VITE_API_URL=""
VITE_API_TOKEN=""
VITE_API_SYNC_URL=""

3
.gitignore vendored
View File

@ -11,7 +11,8 @@ node_modules
dist
dist-content
dist-ssr
auto-post-facebook-extensions
/auto-post-facebook-extensions*
auto-post-facebook-extensions_1.0/
*.local
# Editor directories and files

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

View File

@ -7,9 +7,9 @@
<title>Vite + React + TS</title>
<script type="module" crossorigin src="/assets/popup.js"></script>
<link rel="modulepreload" crossorigin href="/assets/_commonjsHelpers-CqkleIqs.js">
<link rel="modulepreload" crossorigin href="/assets/product-api.service-BCmn_jbQ.js">
<link rel="modulepreload" crossorigin href="/assets/product-api.service-Dn0sYl6J.js">
<link rel="modulepreload" crossorigin href="/assets/message.service-DcR3euAR.js">
<link rel="stylesheet" crossorigin href="/assets/popup-DfztxwQY.css">
<link rel="stylesheet" crossorigin href="/assets/popup-DxX4E5UP.css">
</head>
<body>
<div id="root"></div>

15
package-lock.json generated
View File

@ -27,6 +27,7 @@
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"fs-extra": "^11.3.1",
"lodash": "^4.17.21",
"lucide-react": "^0.540.0",
"p-queue": "^8.1.0",
"react": "^19.1.1",
@ -43,6 +44,7 @@
"@eslint/js": "^9.33.0",
"@types/chrome": "^0.1.4",
"@types/fs-extra": "^11.0.4",
"@types/lodash": "^4.17.20",
"@types/node": "^24.3.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
@ -2575,6 +2577,13 @@
"@types/node": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.3.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
@ -5089,6 +5098,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",

View File

@ -35,6 +35,7 @@
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"fs-extra": "^11.3.1",
"lodash": "^4.17.21",
"lucide-react": "^0.540.0",
"p-queue": "^8.1.0",
"react": "^19.1.1",
@ -51,6 +52,7 @@
"@eslint/js": "^9.33.0",
"@types/chrome": "^0.1.4",
"@types/fs-extra": "^11.0.4",
"@types/lodash": "^4.17.20",
"@types/node": "^24.3.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,9 +7,9 @@
<title>Vite + React + TS</title>
<script type="module" crossorigin src="/assets/popup.js"></script>
<link rel="modulepreload" crossorigin href="/assets/_commonjsHelpers-CqkleIqs.js">
<link rel="modulepreload" crossorigin href="/assets/product-api.service-BCmn_jbQ.js">
<link rel="modulepreload" crossorigin href="/assets/product-api.service-Dn0sYl6J.js">
<link rel="modulepreload" crossorigin href="/assets/message.service-DcR3euAR.js">
<link rel="stylesheet" crossorigin href="/assets/popup-DfztxwQY.css">
<link rel="stylesheet" crossorigin href="/assets/popup-DxX4E5UP.css">
</head>
<body>
<div id="root"></div>

1
server/.env.example Normal file
View File

@ -0,0 +1 @@
BASE_URL = 'https://int.ipsupply.com.au/api/'

3
server/.gitignore vendored
View File

@ -25,3 +25,6 @@ auto-post-facebook-extensions
*.sln
*.sw?
.env
product-cache*
meta.json
data.json

View File

@ -1 +1,20 @@
[]
[
{
"title": "2-Port Analog Telephone Adapter - ATA191-K9",
"price": 220,
"el": {},
"date": "2025-08-21T08:34:00.451Z"
},
{
"title": "Cisco 2911 Voice Bundle, Pvdm3-16, Uc License Pak, Fl- Cube10 - CISCO2911-V/K9",
"price": 125,
"el": {},
"date": "2025-08-21T08:34:00.451Z"
},
{
"title": "Air-ct7510-k9 - AIR-CT7510-K9",
"price": 8562,
"el": {},
"date": "2025-08-21T08:34:00.451Z"
}
]

154
server/package-lock.json generated
View File

@ -9,8 +9,11 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"axios": "^1.11.0",
"cors": "^2.8.5",
"express": "^5.1.0"
"dotenv": "^17.2.1",
"express": "^5.1.0",
"lodash": "^4.17.21"
}
},
"node_modules/accepts": {
@ -26,6 +29,23 @@
"node": ">= 0.6"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
@ -84,6 +104,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/content-disposition": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
@ -153,6 +185,15 @@
}
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -162,6 +203,18 @@
"node": ">= 0.8"
}
},
"node_modules/dotenv": {
"version": "17.2.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
"integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@ -221,6 +274,21 @@
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -295,6 +363,63 @@
"node": ">= 0.8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/form-data/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -383,6 +508,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@ -453,6 +593,12 @@
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT"
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -592,6 +738,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",

View File

@ -12,7 +12,10 @@
"license": "ISC",
"type": "module",
"dependencies": {
"axios": "^1.11.0",
"cors": "^2.8.5",
"express": "^5.1.0"
"dotenv": "^17.2.1",
"express": "^5.1.0",
"lodash": "^4.17.21"
}
}

View File

@ -2,7 +2,17 @@ import express from "express";
import fs from "fs";
import bodyParser from "body-parser";
import cors from "cors";
import axios from "axios";
import path from "path";
import _ from "lodash";
import dotenv from "dotenv";
dotenv.config();
const metaPath = path.join(process.cwd(), "meta.json");
const dataFile = "data.json";
const cachePrefix = "product-cache-";
const app = express();
// Cho phép tất cả origin gọi API
@ -19,14 +29,41 @@ app.post("/sync", (req, res) => {
return res.status(400).json({ error: "Dữ liệu phải là array" });
}
// Thêm field date cho mỗi item
// Chuẩn hoá dữ liệu (có thêm date)
const mapped = data.map((item) => ({
...item,
date: new Date().toISOString(),
}));
// Ghi xuống file
fs.writeFileSync("data.json", JSON.stringify(mapped, null, 2));
// Đọc dữ liệu cũ
let oldData = [];
try {
oldData = JSON.parse(fs.readFileSync(dataFile, "utf-8"));
} catch (e) {
oldData = [];
}
// So sánh (bỏ qua field date vì nó luôn khác)
const stripDate = (arr) => arr.map(({ date, ...rest }) => rest);
const oldStripped = stripDate(oldData);
const newStripped = stripDate(mapped);
if (!_.isEqual(oldStripped, newStripped)) {
// Nếu khác → xoá cache
const files = fs.readdirSync(".");
files.forEach((file) => {
if (file.startsWith(cachePrefix)) {
fs.unlinkSync(path.join(".", file));
}
});
console.log("Dữ liệu thay đổi → xoá cache");
} else {
console.log("Dữ liệu không đổi → giữ cache");
}
// Ghi dữ liệu mới
fs.writeFileSync(dataFile, JSON.stringify(mapped, null, 2));
res.json({ message: "Đã lưu dữ liệu thành công", saved: mapped.length });
});
@ -40,6 +77,91 @@ app.get("/data", (req, res) => {
res.json(JSON.parse(content));
});
app.post("/", async (req, res) => {
try {
// ===== Helpers =====
const readJSON = (path, fallback = null) => {
try {
return JSON.parse(fs.readFileSync(path, "utf-8"));
} catch {
return fallback;
}
};
const writeJSON = (path, data) => {
try {
fs.writeFileSync(path, JSON.stringify(data, null, 2));
} catch (e) {
console.warn("Không ghi được file:", e.message);
}
};
// ===== Meta =====
let meta = readJSON(metaPath, { total: 4000 });
const filter = req.body?.filter || {};
const originalFilter = { ...filter };
const status = filter?.where?.status_listing;
if (["listed", "unlisted"].includes(status)) {
filter.skip = 0;
}
// ===== Cache check =====
const cacheFile = `${cachePrefix}${JSON.stringify(originalFilter)}.json`;
if (fs.existsSync(cacheFile)) {
return res.json(readJSON(cacheFile, {}));
}
// ===== Load publisted data nếu cần =====
let publistedData = [];
if (status && meta.total) {
filter.limit = meta.total;
publistedData = readJSON("data.json", []);
}
// ===== Call API gốc =====
const { data } = await axios({
headers: { Authorization: req.headers.authorization },
url: "transferGetData",
baseURL: process.env.BASE_URL,
method: "POST",
data: { ...req.body, filter },
});
// Update meta
if (typeof data.total === "number") {
writeJSON(metaPath, { total: data.total });
}
// ===== Xử lý listed/unlisted =====
if (status === "listed" || status === "unlisted") {
const skip = originalFilter.skip || 0;
const limit = originalFilter.limit || data.data.length;
const listedCodes = new Set(
publistedData.map((i) => (i.title + i.price || "").trim().toLowerCase())
);
let filteredData = data.data.filter((item) => {
const key = `${item.name} - ${item.code}${item.price}`.toLowerCase();
return status === "listed"
? listedCodes.has(key)
: !listedCodes.has(key);
});
data.total = filteredData.length;
data.data = filteredData.slice(skip, skip + limit);
data.filter = { ...originalFilter, skip, limit };
writeJSON(cacheFile, data);
}
res.json(data);
} catch (err) {
console.error("API error:", err.message);
res.status(500).json({ error: err.message });
}
});
app.listen(3000, () => {
console.log("Server chạy tại http://localhost:3000");
});

View File

@ -21,8 +21,6 @@ class ProductApiService {
};
return axios({
url: "transferGetData",
method: "POST",
data: {
urlAPI: "/api/ebay-listing/listing-get-list",
@ -40,8 +38,6 @@ class ProductApiService {
async get(data: IPost) {
return axios({
url: "transferGetData",
method: "POST",
data: {
urlAPI: "/api/product-model/get-info/" + data.id,
@ -52,7 +48,6 @@ class ProductApiService {
async sync(data: ISyncItem[]) {
return axios({
baseURL: import.meta.env.VITE_API_SYNC_URL,
url: "sync",
method: "POST",
data,
@ -61,7 +56,6 @@ class ProductApiService {
async getPublistedProducts() {
return axios({
baseURL: import.meta.env.VITE_API_SYNC_URL,
url: "data",
});
}

View File

@ -30,7 +30,7 @@ export function ConfirmAlert({
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogContent aria-describedby={undefined}>
<AlertDialogHeader>
<AlertDialogTitle>{title}</AlertDialogTitle>
<AlertDialogDescription>{description}</AlertDialogDescription>

View File

@ -20,7 +20,15 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Select, SelectTrigger, SelectValue } from "@/components/ui/select";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Table,
TableBody,
@ -50,17 +58,16 @@ export function PostTable() {
const msg = new MessageService("popup");
const [searchTerm, setSearchTerm] = useState("");
const [categoryFilter, setCategoryFilter] = useState("all");
const [conditionFilter, setConditionFilter] = useState("all");
const [brandFilter, setBrandFilter] = useState("all");
const [filter, setFilter] = useState<Record<string, any>>({});
const [statusFilter, setStatusFilter] = useState("all");
const [currentPage, setCurrentPage] = useState(1);
const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
const [search] = useDebounce(searchTerm, 400);
const queryKey = useMemo(
() => ["products", { currentPage, search }],
[currentPage, search]
() => ["products", { currentPage, search, filter }],
[currentPage, search, filter]
);
// --- React Query fetch ---
@ -72,6 +79,8 @@ export function PostTable() {
skip: (currentPage - 1) * productApi.item_per_page,
where: {
productModelCode: searchTerm,
status_listing:
filter?.statusFilter === "all" ? undefined : filter.statusFilter,
},
})
);
@ -102,18 +111,15 @@ export function PostTable() {
const clearFilters = () => {
setSearchTerm("");
setCategoryFilter("all");
setConditionFilter("all");
setBrandFilter("all");
setStatusFilter("all");
setFilter({});
setCurrentPage(1);
setIsFilterModalOpen(false);
};
const activeFiltersCount = [
categoryFilter !== "all",
conditionFilter !== "all",
brandFilter !== "all",
searchTerm !== "",
].filter(Boolean).length;
const activeFiltersCount = [statusFilter !== "all", searchTerm !== ""].filter(
Boolean
).length;
const totalPages = useMemo(() => {
if (!rawProducts?.total) return 0;
@ -149,9 +155,13 @@ export function PostTable() {
data: data,
});
} else {
console.log({
a: { ...data, ...mapToIPost({ ...res.data?.data }) },
b: data,
});
msg.send("content", "popup-to-content", {
type: "publist",
data: mapToIPost({ ...data, ...res.data?.data }),
data: { ...data, images: mapToIPost({ ...res.data?.data }).images },
});
}
};
@ -199,27 +209,26 @@ export function PostTable() {
)}
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogContent aria-describedby={undefined} className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Product Filters</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<Select
value={conditionFilter}
onValueChange={setConditionFilter}
value={statusFilter === "all" ? "" : statusFilter}
onValueChange={setStatusFilter}
>
<SelectTrigger>
<SelectValue placeholder="Condition" />
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a status" />
</SelectTrigger>
<SelectContent aria-describedby={undefined}>
<SelectGroup>
<SelectItem value="listed">Listed</SelectItem>
<SelectItem value="unlisted">Unlisted</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
<Select value={brandFilter} onValueChange={setBrandFilter}>
<SelectTrigger>
<SelectValue placeholder="Brand" />
</SelectTrigger>
</Select>
<div className="flex gap-2">
<Button
variant="outline"
@ -229,7 +238,10 @@ export function PostTable() {
Clear Filters
</Button>
<Button
onClick={() => setIsFilterModalOpen(false)}
onClick={() => {
setIsFilterModalOpen(false);
setFilter({ statusFilter });
}}
className="flex-1"
>
Apply

View File

@ -110,9 +110,9 @@ export default function ProductModal({
const watchedTags = form.watch("tags");
const conditions = ["New", "Used - like new", "Used - good", "Used - fair"];
const categories = ["Tools"];
const categories = ["Tools", "Electronics & computers"];
const { isLoading, refetch, ...query } = usePost(data);
const { ...query } = usePost(data);
// const delImageMutation = useMutation({
// mutationFn: async (imageUrl: string) => {
@ -307,7 +307,10 @@ export default function ProductModal({
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="md:max-w-4xl h-[95vh] flex flex-col p-0">
<DialogContent
aria-describedby={undefined}
className="md:max-w-4xl h-[95vh] flex flex-col p-0"
>
{/* Header */}
<DialogHeader className="p-6 border-b">
<DialogTitle className="text-2xl font-bold">
@ -566,7 +569,7 @@ export default function ProductModal({
</FormItem>
)}
/>
<FormField
{/* <FormField
control={form.control}
name="publist"
render={({ field }) => {
@ -600,7 +603,7 @@ export default function ProductModal({
</FormItem>
);
}}
/>
/> */}
</CardContent>
</Card>
</form>
@ -610,9 +613,9 @@ export default function ProductModal({
{/* Footer */}
<div className="p-6 border-t bg-white flex justify-end gap-3">
<Button type="button" variant="outline" onClick={handleClose}>
Cancel
Close
</Button>
<Button
{/* <Button
type="submit"
onClick={form.handleSubmit(onSubmit)}
disabled={form.formState.isSubmitting}
@ -624,7 +627,7 @@ export default function ProductModal({
) : (
"Create"
)}
</Button>
</Button> */}
</div>
</DialogContent>
</Dialog>

View File

@ -15,7 +15,7 @@ msgService.on<{ data: IPost }>(
try {
console.log("[publish] Content nhận:", payload);
await delayRD(1000, 2000);
await delayRD(800, 1000);
await facebookService.handlePublist(payload.data);
} catch (error) {
@ -36,19 +36,19 @@ msgService.on<{ data: IPost }>(
try {
console.log("[unlist] Content nhận:", payload);
await delayRD(1000, 2000);
await delayRD(800, 1000);
await facebookService.gotoSell();
await delayRD(1000, 2000);
await delayRD(800, 1000);
await facebookService.handleDelete(payload.data);
await delayRD(1000, 2000);
await delayRD(800, 1000);
const products = await facebookService.getProducts();
await delayRD(1000, 2000);
await delayRD(800, 1000);
productApi.sync(products);
} catch (error) {
@ -69,11 +69,11 @@ msgService.on<{ data: IPost }>(
try {
console.log("[re-publist] Content nhận:", payload);
await delayRD(1000, 2000);
await delayRD(800, 1000);
await facebookService.gotoSell();
await delayRD(1000, 2000);
await delayRD(800, 1000);
await facebookService.handleRePublist(payload.data);
} catch (error) {
@ -88,7 +88,7 @@ msgService.on<{ data: IPost }>(
);
(async () => {
await delayRD(1000, 2000);
await delayRD(800, 1000);
// Sử dụng
thiefService
@ -101,7 +101,7 @@ msgService.on<{ data: IPost }>(
'[aria-label="Collection of your Marketplace items"]'
);
await delayRD(1000, 2000);
await delayRD(800, 1000);
const products = await facebookService.getProducts();
console.log({ products });

View File

@ -3,7 +3,6 @@ import ax from "axios";
const axios = ax.create({
// Dev
baseURL: import.meta.env.VITE_API_URL,
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + import.meta.env.VITE_API_TOKEN,

View File

@ -35,9 +35,7 @@ export function mapToIPost(raw: any): IPost {
.map((img: any) =>
typeof img === "string"
? img
: `${(import.meta.env.VITE_API_URL as string).replaceAll("api", "")}${
img.url
}`
: `${import.meta.env.VITE_BASE_IMG_URL as string}${img.url}`
)
.slice(0, 10); // chỉ lấy tối đa 10 ảnh
}