Compare commits

...

2 Commits

Author SHA1 Message Date
Admin 805a6361d7 update server 2025-10-06 14:56:46 +07:00
Admin 0165e1ef87 upload server 2025-10-03 15:03:22 +07:00
100 changed files with 9737 additions and 229 deletions

View File

@ -1 +0,0 @@
import{R as c,j as s,Q as u,q as l,P as d}from"./popup-D--aKLqS.js";import"./base64-BbJB1hmJ.js";import"./product-api.service-DR0CE0o9.js";import"./app-C7VSS-q9.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))i(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function n(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(e){if(e.ep)return;e.ep=!0;const r=n(e);fetch(e.href,r)}})();c.createRoot(document.getElementById("root")).render(s.jsx(u,{client:l,children:s.jsx(d,{})}));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,18 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<script type="module" crossorigin src="/assets/popup.js"></script>
<link rel="modulepreload" crossorigin href="/assets/base64-BbJB1hmJ.js">
<link rel="modulepreload" crossorigin href="/assets/product-api.service-DR0CE0o9.js">
<link rel="modulepreload" crossorigin href="/assets/app-C7VSS-q9.js">
<link rel="modulepreload" crossorigin href="/assets/popup-D--aKLqS.js">
<link rel="stylesheet" crossorigin href="/assets/style.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,37 +0,0 @@
{
"manifest_version": 3,
"name": "Auto post facebook extensions",
"version": "1.0",
"action": {
"default_popup": "index.html",
"default_icon": {
"16": "icons/16.png",
"32": "icons/32.png",
"128": "icons/128.png"
}
},
"background": {
"service_worker": "background/background.js",
"type": "module"
},
"host_permissions": ["https://www.facebook.com/*"],
"content_scripts": [
{
"matches": ["https://www.facebook.com/*"],
"js": ["content/content.js"],
"type": "module"
}
],
"web_accessible_resources": [
{
"resources": ["content/inject-ui.js", "assets/*"],
"matches": ["https://www.facebook.com/*"]
}
],
"permissions": ["storage", "tabs", "activeTab", "scripting"],
"icons": {
"16": "icons/16.png",
"32": "icons/32.png",
"128": "icons/128.png"
}
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

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

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

@ -0,0 +1 @@
class s{static ID_EXTENSION="ex-root"}export{s as C};

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

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

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

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

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

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

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

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

View File

@ -1 +1 @@
import{R as c,j as s,Q as u,q as l,P as d}from"./popup-7ET7snnx.js";import"./_commonjsHelpers-CqkleIqs.js";import"./contants-D9-9W2RY.js";import"./app-C7VSS-q9.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))i(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function n(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(e){if(e.ep)return;e.ep=!0;const r=n(e);fetch(e.href,r)}})();c.createRoot(document.getElementById("root")).render(s.jsx(u,{client:l,children:s.jsx(d,{})}));
import{R as c,j as s,Q as u,q as l,P as d}from"./popup-D7Kw3eOc.js";import"./_commonjsHelpers-CqkleIqs.js";import"./app-BbB6QE4i.js";import"./contants-Ds_B-epI.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))i(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function n(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(e){if(e.ep)return;e.ep=!0;const r=n(e);fetch(e.href,r)}})();c.createRoot(document.getElementById("root")).render(s.jsx(u,{client:l,children:s.jsx(d,{})}));

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

@ -1 +1 @@
import{r as m,j as o,T as b,R as j,Q as E,q as T,P as C}from"../assets/popup-7ET7snnx.js";import{C as w}from"../assets/contants-D9-9W2RY.js";import"../assets/_commonjsHelpers-CqkleIqs.js";import"../assets/app-C7VSS-q9.js";var I=(t,n,d,a,i,r,u,p)=>{let s=document.documentElement,h=["light","dark"];function c(e){(Array.isArray(t)?t:[t]).forEach(l=>{let g=l==="class",S=g&&r?i.map(y=>r[y]||y):i;g?(s.classList.remove(...S),s.classList.add(r&&r[e]?r[e]:e)):s.setAttribute(l,e)}),x(e)}function x(e){p&&h.includes(e)&&(s.style.colorScheme=e)}function v(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}if(a)c(a);else try{let e=localStorage.getItem(n)||d,l=u&&e==="system"?v():e;c(l)}catch{}},N=m.createContext(void 0),R={setTheme:t=>{},themes:[]},k=()=>{var t;return(t=m.useContext(N))!=null?t:R};m.memo(({forcedTheme:t,storageKey:n,attribute:d,enableSystem:a,enableColorScheme:i,defaultTheme:r,value:u,themes:p,nonce:s,scriptProps:h})=>{let c=JSON.stringify([d,n,r,t,p,u,a,i]).slice(1,-1);return m.createElement("script",{...h,suppressHydrationWarning:!0,nonce:typeof window>"u"?s:"",dangerouslySetInnerHTML:{__html:`(${I.toString()})(${c})`}})});const M=({...t})=>{const{theme:n="system"}=k();return o.jsx(b,{theme:n,className:"toaster group",style:{"--normal-bg":"var(--popover)","--normal-text":"var(--popover-foreground)","--normal-border":"var(--border)"},...t})},f=document.getElementById(w.ID_EXTENSION);f&&j.createRoot(f).render(o.jsx(o.Fragment,{children:o.jsxs(E,{client:T,children:[o.jsx("div",{className:"fixed bottom-20 right-6 ex-root",children:o.jsx(C,{})}),o.jsx(M,{position:"top-right"})]})}));
import{r as m,j as o,T as b,R as j,Q as E,q as T,P as C}from"../assets/popup-D7Kw3eOc.js";import{C as w}from"../assets/contants-Ds_B-epI.js";import"../assets/_commonjsHelpers-CqkleIqs.js";import"../assets/app-BbB6QE4i.js";var I=(t,n,d,a,i,r,u,p)=>{let s=document.documentElement,h=["light","dark"];function c(e){(Array.isArray(t)?t:[t]).forEach(l=>{let g=l==="class",S=g&&r?i.map(y=>r[y]||y):i;g?(s.classList.remove(...S),s.classList.add(r&&r[e]?r[e]:e)):s.setAttribute(l,e)}),x(e)}function x(e){p&&h.includes(e)&&(s.style.colorScheme=e)}function v(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}if(a)c(a);else try{let e=localStorage.getItem(n)||d,l=u&&e==="system"?v():e;c(l)}catch{}},N=m.createContext(void 0),R={setTheme:t=>{},themes:[]},k=()=>{var t;return(t=m.useContext(N))!=null?t:R};m.memo(({forcedTheme:t,storageKey:n,attribute:d,enableSystem:a,enableColorScheme:i,defaultTheme:r,value:u,themes:p,nonce:s,scriptProps:h})=>{let c=JSON.stringify([d,n,r,t,p,u,a,i]).slice(1,-1);return m.createElement("script",{...h,suppressHydrationWarning:!0,nonce:typeof window>"u"?s:"",dangerouslySetInnerHTML:{__html:`(${I.toString()})(${c})`}})});const M=({...t})=>{const{theme:n="system"}=k();return o.jsx(b,{theme:n,className:"toaster group",style:{"--normal-bg":"var(--popover)","--normal-text":"var(--popover-foreground)","--normal-border":"var(--border)"},...t})},f=document.getElementById(w.ID_EXTENSION);f&&j.createRoot(f).render(o.jsx(o.Fragment,{children:o.jsxs(E,{client:T,children:[o.jsx("div",{className:"fixed bottom-20 right-6 ex-root",children:o.jsx(C,{})}),o.jsx(M,{position:"top-right"})]})}));

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/contants-D9-9W2RY.js">
<link rel="modulepreload" crossorigin href="/assets/app-C7VSS-q9.js">
<link rel="modulepreload" crossorigin href="/assets/popup-7ET7snnx.js">
<link rel="modulepreload" crossorigin href="/assets/app-BbB6QE4i.js">
<link rel="modulepreload" crossorigin href="/assets/contants-Ds_B-epI.js">
<link rel="modulepreload" crossorigin href="/assets/popup-D7Kw3eOc.js">
<link rel="stylesheet" crossorigin href="/assets/style.css">
</head>
<body>

1
server/server-v2 Submodule

@ -0,0 +1 @@
Subproject commit e7fd818c75881047115599e0227d093b4a460f4e

View File

@ -4,52 +4,53 @@ import type { IPost, ISyncItem } from "@/lib/utils";
class ProductApiService {
item_per_page = 10;
async index(filter?: {
skip?: number;
limit?: number;
order?: string;
where?: Record<string, any>;
info?: any;
}) {
const defaultFilter = {
skip: 0,
limit: 10,
order: "updatedAt desc",
where: {
account: "prology_net",
status: "Updated",
},
};
async index(params?: Record<string, any>) {
return axios({
method: "POST",
data: {
urlAPI: "/api/ebay-listing/listing-get-list",
filter: {
...defaultFilter,
...filter, // merge filter vào defaultFilter
where: {
...defaultFilter.where,
...filter?.where, // merge where riêng để không mất account/status mặc định
},
},
},
method: "GET",
url: "products",
params,
});
}
async get(data: IPost) {
// return axios({
// method: "POST",
// data: {
// filter: {
// where: {
// account: "prology_net",
// condition: data.raw_condition,
// productModelCode: data.sku,
// },
// },
// pageCurrent: "/ebaytools/listing-ebay",
// urlAPI: "/api/ebay-listing/get-image-listing",
// },
// });
return axios({
method: "GET",
url: "products/" + data.ebayListingId,
});
}
async publish(data: IPost) {
return axios({
method: "POST",
url: "products/publish/" + data.ebayListingId,
data: {
filter: {
where: {
account: "prology_net",
condition: data.raw_condition,
productModelCode: data.sku,
},
},
pageCurrent: "/ebaytools/listing-ebay",
urlAPI: "/api/ebay-listing/get-image-listing",
info: (data as any).info || {},
hash_info: (data as any).hash_info,
},
});
}
async unlist(data: IPost) {
return axios({
method: "POST",
url: "products/unlist/" + data.ebayListingId,
data: {
info: (data as any).info || {},
hash_info: (data as any).hash_info,
},
});
}
@ -77,9 +78,10 @@ class ProductApiService {
});
}
async getPublistedProducts() {
async getPublistedProducts(info: string) {
return axios({
url: "data",
params: { info },
});
}

View File

@ -40,7 +40,7 @@ export function ImprovedToggleFilter({
<ToggleGroupItem
size={"sm"}
value="unlisted"
value="unlist"
aria-label="Ẩn khỏi danh sách"
className="flex items-center gap-2 px-3 py-2 rounded-md data-[state=on]:bg-orange-100 data-[state=on]:text-orange-700 data-[state=on]:border-orange-200 hover:bg-orange-50 transition-colors"
>

View File

@ -37,12 +37,10 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { delay } from "@/features/app";
import useActionPostMutation from "@/hooks/use-action-post-mutation";
import usePost from "@/hooks/use-post-hook";
import { mapToIPost, type IPost } from "@/lib/utils";
import { useMutation } from "@tanstack/react-query";
import { toast } from "sonner";
import z from "zod/v3";
// eslint-disable-next-line react-refresh/only-export-components
@ -178,25 +176,25 @@ export default function ProductModal({
})();
}, [query.data, data]);
const saveMutation = useMutation({
mutationKey: ["save_product"],
mutationFn: async (values: any) => {
await delay(300);
return productApi.apiRequest("saveProductLocalServer", {
id: data?.id,
mapped: true,
...values,
});
},
onSuccess() {
toast.success("Saved", {
description: (
<span className="!text-black font-medium">{"Product was saved"}</span>
),
});
refetch();
},
});
// const saveMutation = useMutation({
// mutationKey: ["save_product"],
// mutationFn: async (values: any) => {
// await delay(300);
// return productApi.apiRequest("saveProductLocalServer", {
// id: data?.id,
// mapped: true,
// ...values,
// });
// },
// onSuccess() {
// toast.success("Saved", {
// description: (
// <span className="!text-black font-medium">{"Product was saved"}</span>
// ),
// });
// refetch();
// },
// });
return (
<Dialog open={open} onOpenChange={setOpen}>

View File

@ -35,7 +35,7 @@ function DropdownMenuContent({
container,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content> & {
container: Element;
container?: Element;
}) {
return (
<DropdownMenuPrimitive.Portal container={container}>

View File

@ -3,7 +3,7 @@ import { productApi } from "@/api/product-api.service";
import { delayRD } from "@/features/app";
import { Contants } from "@/lib/contants";
import { Queue } from "@/lib/queue";
import type { IPost } from "@/lib/utils";
import { hashInfo, type IPost } from "@/lib/utils";
import { facebookService } from "@/services/facebook.service";
import MessageService from "@/services/message.service";
import { thiefService } from "@/services/thief.service";
@ -160,12 +160,19 @@ window.addEventListener("message", async (event) => {
case "index": {
const info = await facebookService.getInfo();
const response = await productApi.index({ info, ...body });
const hash = await hashInfo(info);
const response = await productApi.index({
"filter.info": hash,
...body,
});
data = response.data;
break;
}
case "getPublistedProducts": {
const response = await productApi.getPublistedProducts();
const info = await facebookService.getInfo();
const response = await productApi.getPublistedProducts(info);
data = response.data;
break;
}
@ -173,8 +180,6 @@ window.addEventListener("message", async (event) => {
const response = await productApi.get(body);
data = response.data;
console.log({ data, body });
break;
}
case "createBlobUrl": {
@ -188,12 +193,18 @@ window.addEventListener("message", async (event) => {
"background-to-content-create-blod-urls"
);
console.log({ data });
break;
}
case "publist": {
const info = await facebookService.getInfo();
const hash = await hashInfo(info);
msgService.send("background", "content-to-background-actions", {
type: "publist",
data: body,
data: { ...body, info, hash_info: hash },
});
data = await msgService.waitForMessage(
@ -204,9 +215,13 @@ window.addEventListener("message", async (event) => {
}
case "unlist": {
const info = await facebookService.getInfo();
const hash = await hashInfo(info);
msgService.send("background", "content-to-background-actions", {
type: "unlist",
data: body,
data: { ...body, info, hash_info: hash },
});
data = await msgService.waitForMessage(
@ -216,10 +231,9 @@ window.addEventListener("message", async (event) => {
break;
}
case "sync": {
const el = await thiefService.waitForElement(
await thiefService.waitForElement(
facebookService.selectors.collection_marketplace
);
console.log("Element đã xuất hiện:", el);
await facebookService.waitForPageReady(
facebookService.sellingPath,
@ -244,8 +258,6 @@ window.addEventListener("message", async (event) => {
const response = await productApi.getProductOnLocalServer(body);
data = response.data;
console.log({ data, body });
break;
}
@ -253,8 +265,6 @@ window.addEventListener("message", async (event) => {
const response = await productApi.saveProductOnLocalServer(body);
data = response.data;
console.log({ data, body });
break;
}
}
@ -268,6 +278,8 @@ window.addEventListener("message", async (event) => {
"*"
);
} catch (error) {
console.log({ error });
window.postMessage(
{
type: "API_RESPONSE",
@ -320,24 +332,24 @@ const runSync = () => {
// tiện ích khởi chạy sync + app
const startApp = () => {
runSync();
// runSync();
injectApp();
};
if (window.location.href.includes(facebookService.sellingPath)) {
try {
await delayRD(800, 1000);
// if (window.location.href.includes(facebookService.sellingPath)) {
// try {
// await delayRD(800, 1000);
const el = await thiefService.waitForElement(
facebookService.selectors.collection_marketplace
);
console.log("Element đã xuất hiện:", el);
// const el = await thiefService.waitForElement(
// facebookService.selectors.collection_marketplace
// );
// console.log("Element đã xuất hiện:", el);
await delayRD(800, 1000);
} catch (err) {
console.error(err);
}
}
// await delayRD(800, 1000);
// } catch (err) {
// console.error(err);
// }
// }
startApp();
})();

View File

@ -2,11 +2,11 @@ import { productApi } from "@/api/product-api.service";
import { mapToIPost, type IPost } from "@/lib/utils";
import { useMutation } from "@tanstack/react-query";
const useActionPostMutation = () => {
const useActionPostMutation = (options?: { onSetted: () => void }) => {
return useMutation({
mutationKey: ["action-mutaions"],
mutationFn: async (data: IPost) => {
if (data.status) {
if (data.listedProducts?.length) {
return productApi.apiRequest("unlist", data);
}
@ -28,6 +28,9 @@ const useActionPostMutation = () => {
images: mapToIPost({ ...(res as any)?.data }).images,
});
},
onSettled() {
options?.onSetted?.();
},
});
};

View File

@ -18,6 +18,8 @@ export interface IPost {
raw_condition?: string;
base64Images?: string[];
mapped?: boolean;
listedProducts?: { id: string }[] | null;
ebayListingId: string;
}
export interface ISyncItem {
@ -90,5 +92,15 @@ export function mapToIPost(raw: any): IPost {
publist_id: raw?.listingId,
raw_condition: raw?.condition,
mapped: true,
listedProducts: raw?.listedProducts || null,
ebayListingId: raw?.ebayListingId,
};
}
export async function hashInfo(info: any) {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(info));
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
}

View File

@ -23,11 +23,11 @@ import {
TableRow,
} from "@/components/ui/table";
import type { Noti } from "@/content/content";
import { removeFalsyValues } from "@/features/app";
import { delay, removeFalsyValues } from "@/features/app";
import useActionPostMutation from "@/hooks/use-action-post-mutation";
import { useShadowPortal } from "@/hooks/use-shadow-portal";
import { mapToIPost, type IPost } from "@/lib/utils";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useQuery } from "@tanstack/react-query";
import {
ChevronLeft,
ChevronRight,
@ -35,6 +35,7 @@ import {
Eye,
EyeOff,
MoreHorizontal,
RefreshCcw,
Search,
} from "lucide-react";
import { useEffect, useMemo, useState } from "react";
@ -58,35 +59,38 @@ export default function Popup() {
);
// --- React Query fetch ---
const { data: rawProducts, isFetching } = useQuery({
const {
data: rawProducts,
isFetching,
refetch,
} = useQuery({
queryKey,
queryFn: async () => {
await delay(300);
const data = await productApi.apiRequest(
"index",
removeFalsyValues({
skip: (currentPage - 1) * productApi.item_per_page,
where: {
productModelCode: searchTerm,
status_listing:
filter?.statusFilter === "all" ? undefined : filter.statusFilter,
},
page: currentPage,
search: searchTerm,
["filter.status"]: filter.statusFilter || undefined,
})
);
return data;
},
});
const { data: publistedProducts } = useQuery({
queryKey: ["publised-products"],
queryFn: async () => {
const data = await productApi.apiRequest("getPublistedProducts", {});
return data ?? [];
},
staleTime: 0, // luôn coi là stale -> gọi lại API mỗi lần mount
refetchOnMount: "always",
});
// const { data: publistedProducts } = useQuery({
// queryKey: ["publised-products"],
// queryFn: async () => {
// const data = await productApi.apiRequest("getPublistedProducts", {});
// return data ?? [];
// },
// staleTime: 0, // luôn coi là stale -> gọi lại API mỗi lần mount
// refetchOnMount: "always",
// });
const actionMutation = useActionPostMutation();
const actionMutation = useActionPostMutation({ onSetted: refetch });
const data: IPost[] = useMemo(() => {
if (!rawProducts || !(rawProducts as any)?.data) return [];
@ -119,17 +123,6 @@ export default function Popup() {
// --- reset page when filter changes ---
// eslint-disable-next-line react-hooks/exhaustive-deps
const from = useMemo(() => {
return (currentPage - 1) * productApi.item_per_page + 1;
}, [currentPage]);
const to = useMemo(() => {
return Math.min(
currentPage * productApi.item_per_page,
(rawProducts as any)?.total ?? 0
);
}, [currentPage, rawProducts]);
const handleActionListing = async (data: IPost) => {
actionMutation.mutate(data);
};
@ -179,6 +172,7 @@ export default function Popup() {
}
};
}, [open]);
return (
<Popover open={open} onOpenChange={setOpen}>
<TrigerOpenModal />
@ -201,17 +195,48 @@ export default function Popup() {
/>
</div>
<ImprovedToggleFilter
filter={filter as any}
setFilter={setFilter}
activeFiltersCount={activeFiltersCount}
clearFilters={clearFilters}
/>
<div className="flex items-center gap-2">
<ImprovedToggleFilter
filter={filter as any}
setFilter={setFilter}
activeFiltersCount={activeFiltersCount}
clearFilters={clearFilters}
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="w-full flex items-center justify-center">
<Button
variant="ghost"
size="sm"
className="h-8 w-8 p-0 mx-auto"
>
<MoreHorizontal className="h-4 w-4" />
</Button>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent
container={portalContainer as Element}
align="end"
>
<DropdownMenuItem
onSelect={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onClick={() => refetch()}
>
<RefreshCcw className="h-4 w-4 mr-2" /> Refresh
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<div className="flex items-center justify-between w-full gap-8">
<span>
Showing {from}-{to} of {(rawProducts as any)?.total ?? 0} products
Showing {(rawProducts as any)?.from}-{(rawProducts as any)?.to} of{" "}
{(rawProducts as any)?.total ?? 0} products
</span>
{totalPages > 1 && (
<div className="flex items-center justify-end gap-2">
@ -320,7 +345,12 @@ export default function Popup() {
</div>
<div className="rounded-md border overflow-x-auto relative">
<ScrollArea className="h-[66vh] w-full">
<ScrollArea className="h-[66vh] w-full relative">
{isFetching && (
<div className="h-full flex items-center justify-center w-full absolute inset-0 bg-[rgba(0,0,0,.1)] z-20 ">
<Loader size="size-6" />
</div>
)}
<Table className="h-fit">
<TableHeader>
<TableRow>
@ -334,19 +364,6 @@ export default function Popup() {
</TableHeader>
<TableBody className="relative">
{isFetching && (
<TableRow>
<TableCell
colSpan={10}
className="text-center py-8 text-muted-foreground "
>
<div className="h-full flex items-center justify-center w-full">
<Loader />
</div>
</TableCell>
</TableRow>
)}
{data.length === 0 && !isFetching ? (
<TableRow>
<TableCell
@ -360,12 +377,6 @@ export default function Popup() {
</TableRow>
) : (
data.map((post) => {
const status = (publistedProducts as any)?.some(
(item: any) => item.title.includes(post.sku)
);
post.status = status;
return (
<TableRow key={post.id}>
<TableCell className="font-medium">
@ -387,7 +398,9 @@ export default function Popup() {
</TableCell>
<TableCell>
<Badge variant="secondary">
{post?.status ? "Listed" : "Unlisted"}
{post?.listedProducts?.length
? "Listed"
: "Unlisted"}
</Badge>
</TableCell>
@ -421,12 +434,14 @@ export default function Popup() {
<DropdownMenuItem
onClick={() => handleActionListing(post)}
>
{post.status ? (
{post.listedProducts?.length ? (
<EyeOff className="h-4 w-4 mr-2" />
) : (
<Eye className="h-4 w-4 mr-2" />
)}
{post.status ? "Unlist" : "List"}
{post.listedProducts?.length
? "Unlist"
: "List"}
{/* {actionMutation.isPending && <Loader />} */}
</DropdownMenuItem>

View File

@ -2,6 +2,7 @@
import { delay, delayRD } from "@/features/app";
import type { IPost, ISyncItem } from "@/lib/utils";
import { thiefService } from "./thief.service";
import { productApi } from "@/api/product-api.service";
class FacebookService {
sellingPath = "https://www.facebook.com/marketplace/you/selling";
@ -266,10 +267,18 @@ class FacebookService {
await this.clickNext();
// await delayRD(300, 500);
await delayRD(300, 500);
// await this.clickPublist();
console.log({ abc: document?.querySelector('[aria-label="Publish"]') });
document
?.querySelector('[aria-label="Publish"]')
?.addEventListener("click", async () => {
await productApi.publish(item);
});
return true;
};
@ -541,7 +550,7 @@ class FacebookService {
closeBtnModalFeedback?.click();
// Gọi API backend để đánh dấu sản phẩm đã xóa (published = false)
// await productApi.finistDelete(payload, { published: false });
await productApi.unlist(payload);
// Gửi message đến background/content script thông báo là delete đã xong
chrome.runtime.sendMessage({ type: "delete-done" });