From 3a9a4d092d4df009870f08e6357eac89b2f15967 Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 14 Aug 2025 14:55:45 +0700 Subject: [PATCH] update --- .../content.js | 2 +- .../src/content.ts | 98 +++++++++++++------ client/app/components/core/data-table.tsx | 6 +- client/app/components/nav-projects.tsx | 18 ++-- client/app/layouts/private-layout.tsx | 5 +- client/app/routes/products/list.tsx | 16 ++- .../modules/products/products.controller.ts | 5 + .../src/modules/products/products.service.ts | 64 ++++++++---- 8 files changed, 147 insertions(+), 67 deletions(-) diff --git a/auto-listing-facebook-marketplace/auto-listing-facebook-marketplace/content.js b/auto-listing-facebook-marketplace/auto-listing-facebook-marketplace/content.js index 968533f..fbe64c7 100644 --- a/auto-listing-facebook-marketplace/auto-listing-facebook-marketplace/content.js +++ b/auto-listing-facebook-marketplace/auto-listing-facebook-marketplace/content.js @@ -3,4 +3,4 @@ function Ne(e,t){return function(){return e.apply(t,arguments)}}const{toString:r `)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const i=new this(t);return n.forEach(r=>i.set(r)),i}static accessor(t){const i=(this[ge]=this[ge]={accessors:{}}).accessors,r=this.prototype;function o(s){const d=I(s);i[d]||(un(r,s),i[d]=!0)}return a.isArray(t)?t.forEach(o):o(t),this}};C.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);a.reduceDescriptors(C.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(i){this[n]=i}}});a.freezeMethods(C);function re(e,t){const n=this||$,i=t||n,r=C.from(i.headers);let o=i.data;return a.forEach(e,function(d){o=d.call(n,o,r.normalize(),t?t.status:void 0)}),r.normalize(),o}function Xe(e){return!!(e&&e.__CANCEL__)}function q(e,t,n){h.call(this,e??"canceled",h.ERR_CANCELED,t,n),this.name="CanceledError"}a.inherits(q,h,{__CANCEL__:!0});function Ke(e,t,n){const i=n.config.validateStatus;!n.status||!i||i(n.status)?e(n):t(new h("Request failed with status code "+n.status,[h.ERR_BAD_REQUEST,h.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function fn(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function pn(e,t){e=e||10;const n=new Array(e),i=new Array(e);let r=0,o=0,s;return t=t!==void 0?t:1e3,function(u){const c=Date.now(),l=i[o];s||(s=c),n[r]=u,i[r]=c;let p=o,y=0;for(;p!==r;)y+=n[p++],p=p%e;if(r=(r+1)%e,r===o&&(o=(o+1)%e),c-s{n=l,r=null,o&&(clearTimeout(o),o=null),e.apply(null,c)};return[(...c)=>{const l=Date.now(),p=l-n;p>=i?s(c,l):(r=c,o||(o=setTimeout(()=>{o=null,s(r)},i-p)))},()=>r&&s(r)]}const K=(e,t,n=3)=>{let i=0;const r=pn(50,250);return vn(o=>{const s=o.loaded,d=o.lengthComputable?o.total:void 0,u=s-i,c=r(u),l=s<=d;i=s;const p={loaded:s,total:d,progress:d?s/d:void 0,bytes:u,rate:c||void 0,estimated:c&&d&&l?(d-s)/c:void 0,event:o,lengthComputable:d!=null,[t?"download":"upload"]:!0};e(p)},n)},Re=(e,t)=>{const n=e!=null;return[i=>t[0]({lengthComputable:n,total:e,loaded:i}),t[1]]},Se=e=>(...t)=>a.asap(()=>e(...t)),hn=O.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,O.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(O.origin),O.navigator&&/(msie|trident)/i.test(O.navigator.userAgent)):()=>!0,mn=O.hasStandardBrowserEnv?{write(e,t,n,i,r,o){const s=[e+"="+encodeURIComponent(t)];a.isNumber(n)&&s.push("expires="+new Date(n).toGMTString()),a.isString(i)&&s.push("path="+i),a.isString(r)&&s.push("domain="+r),o===!0&&s.push("secure"),document.cookie=s.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function wn(e){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function yn(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function We(e,t,n){let i=!wn(t);return e&&(i||n==!1)?yn(e,t):t}const Te=e=>e instanceof C?{...e}:e;function D(e,t){t=t||{};const n={};function i(c,l,p,y){return a.isPlainObject(c)&&a.isPlainObject(l)?a.merge.call({caseless:y},c,l):a.isPlainObject(l)?a.merge({},l):a.isArray(l)?l.slice():l}function r(c,l,p,y){if(a.isUndefined(l)){if(!a.isUndefined(c))return i(void 0,c,p,y)}else return i(c,l,p,y)}function o(c,l){if(!a.isUndefined(l))return i(void 0,l)}function s(c,l){if(a.isUndefined(l)){if(!a.isUndefined(c))return i(void 0,c)}else return i(void 0,l)}function d(c,l,p){if(p in t)return i(c,l);if(p in e)return i(void 0,c)}const u={url:o,method:o,data:o,baseURL:s,transformRequest:s,transformResponse:s,paramsSerializer:s,timeout:s,timeoutMessage:s,withCredentials:s,withXSRFToken:s,adapter:s,responseType:s,xsrfCookieName:s,xsrfHeaderName:s,onUploadProgress:s,onDownloadProgress:s,decompress:s,maxContentLength:s,maxBodyLength:s,beforeRedirect:s,transport:s,httpAgent:s,httpsAgent:s,cancelToken:s,socketPath:s,responseEncoding:s,validateStatus:d,headers:(c,l,p)=>r(Te(c),Te(l),p,!0)};return a.forEach(Object.keys(Object.assign({},e,t)),function(l){const p=u[l]||r,y=p(e[l],t[l],l);a.isUndefined(y)&&p!==d||(n[l]=y)}),n}const Ge=e=>{const t=D({},e);let{data:n,withXSRFToken:i,xsrfHeaderName:r,xsrfCookieName:o,headers:s,auth:d}=t;t.headers=s=C.from(s),t.url=ze(We(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),d&&s.set("Authorization","Basic "+btoa((d.username||"")+":"+(d.password?unescape(encodeURIComponent(d.password)):"")));let u;if(a.isFormData(n)){if(O.hasStandardBrowserEnv||O.hasStandardBrowserWebWorkerEnv)s.setContentType(void 0);else if((u=s.getContentType())!==!1){const[c,...l]=u?u.split(";").map(p=>p.trim()).filter(Boolean):[];s.setContentType([c||"multipart/form-data",...l].join("; "))}}if(O.hasStandardBrowserEnv&&(i&&a.isFunction(i)&&(i=i(t)),i||i!==!1&&hn(t.url))){const c=r&&o&&mn.read(o);c&&s.set(r,c)}return t},bn=typeof XMLHttpRequest<"u",En=bn&&function(e){return new Promise(function(n,i){const r=Ge(e);let o=r.data;const s=C.from(r.headers).normalize();let{responseType:d,onUploadProgress:u,onDownloadProgress:c}=r,l,p,y,R,f;function m(){R&&R(),f&&f(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener("abort",l)}let v=new XMLHttpRequest;v.open(r.method.toUpperCase(),r.url,!0),v.timeout=r.timeout;function E(){if(!v)return;const T=C.from("getAllResponseHeaders"in v&&v.getAllResponseHeaders()),_={data:!d||d==="text"||d==="json"?v.responseText:v.response,status:v.status,statusText:v.statusText,headers:T,config:e,request:v};Ke(function(L){n(L),m()},function(L){i(L),m()},_),v=null}"onloadend"in v?v.onloadend=E:v.onreadystatechange=function(){!v||v.readyState!==4||v.status===0&&!(v.responseURL&&v.responseURL.indexOf("file:")===0)||setTimeout(E)},v.onabort=function(){v&&(i(new h("Request aborted",h.ECONNABORTED,e,v)),v=null)},v.onerror=function(){i(new h("Network Error",h.ERR_NETWORK,e,v)),v=null},v.ontimeout=function(){let N=r.timeout?"timeout of "+r.timeout+"ms exceeded":"timeout exceeded";const _=r.transitional||Je;r.timeoutErrorMessage&&(N=r.timeoutErrorMessage),i(new h(N,_.clarifyTimeoutError?h.ETIMEDOUT:h.ECONNABORTED,e,v)),v=null},o===void 0&&s.setContentType(null),"setRequestHeader"in v&&a.forEach(s.toJSON(),function(N,_){v.setRequestHeader(_,N)}),a.isUndefined(r.withCredentials)||(v.withCredentials=!!r.withCredentials),d&&d!=="json"&&(v.responseType=r.responseType),c&&([y,f]=K(c,!0),v.addEventListener("progress",y)),u&&v.upload&&([p,R]=K(u),v.upload.addEventListener("progress",p),v.upload.addEventListener("loadend",R)),(r.cancelToken||r.signal)&&(l=T=>{v&&(i(!T||T.type?new q(null,e,v):T),v.abort(),v=null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener("abort",l)));const S=fn(r.url);if(S&&O.protocols.indexOf(S)===-1){i(new h("Unsupported protocol "+S+":",h.ERR_BAD_REQUEST,e));return}v.send(o||null)})},gn=(e,t)=>{const{length:n}=e=e?e.filter(Boolean):[];if(t||n){let i=new AbortController,r;const o=function(c){if(!r){r=!0,d();const l=c instanceof Error?c:this.reason;i.abort(l instanceof h?l:new q(l instanceof Error?l.message:l))}};let s=t&&setTimeout(()=>{s=null,o(new h(`timeout ${t} of ms exceeded`,h.ETIMEDOUT))},t);const d=()=>{e&&(s&&clearTimeout(s),s=null,e.forEach(c=>{c.unsubscribe?c.unsubscribe(o):c.removeEventListener("abort",o)}),e=null)};e.forEach(c=>c.addEventListener("abort",o));const{signal:u}=i;return u.unsubscribe=()=>a.asap(d),u}},Rn=function*(e,t){let n=e.byteLength;if(n{const r=Sn(e,t);let o=0,s,d=u=>{s||(s=!0,i&&i(u))};return new ReadableStream({async pull(u){try{const{done:c,value:l}=await r.next();if(c){d(),u.close();return}let p=l.byteLength;if(n){let y=o+=p;n(y)}u.enqueue(new Uint8Array(l))}catch(c){throw d(c),c}},cancel(u){return d(u),r.return()}},{highWaterMark:2})},te=typeof fetch=="function"&&typeof Request=="function"&&typeof Response=="function",Ze=te&&typeof ReadableStream=="function",An=te&&(typeof TextEncoder=="function"?(e=>t=>e.encode(t))(new TextEncoder):async e=>new Uint8Array(await new Response(e).arrayBuffer())),Qe=(e,...t)=>{try{return!!e(...t)}catch{return!1}},On=Ze&&Qe(()=>{let e=!1;const t=new Request(O.origin,{body:new ReadableStream,method:"POST",get duplex(){return e=!0,"half"}}).headers.has("Content-Type");return e&&!t}),Oe=64*1024,ce=Ze&&Qe(()=>a.isReadableStream(new Response("").body)),W={stream:ce&&(e=>e.body)};te&&(e=>{["text","arrayBuffer","blob","formData","stream"].forEach(t=>{!W[t]&&(W[t]=a.isFunction(e[t])?n=>n[t]():(n,i)=>{throw new h(`Response type '${t}' is not supported`,h.ERR_NOT_SUPPORT,i)})})})(new Response);const _n=async e=>{if(e==null)return 0;if(a.isBlob(e))return e.size;if(a.isSpecCompliantForm(e))return(await new Request(O.origin,{method:"POST",body:e}).arrayBuffer()).byteLength;if(a.isArrayBufferView(e)||a.isArrayBuffer(e))return e.byteLength;if(a.isURLSearchParams(e)&&(e=e+""),a.isString(e))return(await An(e)).byteLength},xn=async(e,t)=>{const n=a.toFiniteNumber(e.getContentLength());return n??_n(t)},Cn=te&&(async e=>{let{url:t,method:n,data:i,signal:r,cancelToken:o,timeout:s,onDownloadProgress:d,onUploadProgress:u,responseType:c,headers:l,withCredentials:p="same-origin",fetchOptions:y}=Ge(e);c=c?(c+"").toLowerCase():"text";let R=gn([r,o&&o.toAbortSignal()],s),f;const m=R&&R.unsubscribe&&(()=>{R.unsubscribe()});let v;try{if(u&&On&&n!=="get"&&n!=="head"&&(v=await xn(l,i))!==0){let _=new Request(t,{method:"POST",body:i,duplex:"half"}),B;if(a.isFormData(i)&&(B=_.headers.get("content-type"))&&l.setContentType(B),_.body){const[L,z]=Re(v,K(Se(u)));i=Ae(_.body,Oe,L,z)}}a.isString(p)||(p=p?"include":"omit");const E="credentials"in Request.prototype;f=new Request(t,{...y,signal:R,method:n.toUpperCase(),headers:l.normalize().toJSON(),body:i,duplex:"half",credentials:E?p:void 0});let S=await fetch(f,y);const T=ce&&(c==="stream"||c==="response");if(ce&&(d||T&&m)){const _={};["status","statusText","headers"].forEach(me=>{_[me]=S[me]});const B=a.toFiniteNumber(S.headers.get("content-length")),[L,z]=d&&Re(B,K(Se(d),!0))||[];S=new Response(Ae(S.body,Oe,L,()=>{z&&z(),m&&m()}),_)}c=c||"text";let N=await W[a.findKey(W,c)||"text"](S,e);return!T&&m&&m(),await new Promise((_,B)=>{Ke(_,B,{data:N,headers:C.from(S.headers),status:S.status,statusText:S.statusText,config:e,request:f})})}catch(E){throw m&&m(),E&&E.name==="TypeError"&&/Load failed|fetch/i.test(E.message)?Object.assign(new h("Network Error",h.ERR_NETWORK,e,f),{cause:E.cause||E}):h.from(E,E&&E.code,e,f)}}),le={http:$t,xhr:En,fetch:Cn};a.forEach(le,(e,t)=>{if(e){try{Object.defineProperty(e,"name",{value:t})}catch{}Object.defineProperty(e,"adapterName",{value:t})}});const _e=e=>`- ${e}`,Pn=e=>a.isFunction(e)||e===null||e===!1,Ye={getAdapter:e=>{e=a.isArray(e)?e:[e];const{length:t}=e;let n,i;const r={};for(let o=0;o`adapter ${d} `+(u===!1?"is not supported by the environment":"is not available in the build"));let s=t?o.length>1?`since : `+o.map(_e).join(` `):" "+_e(o[0]):"as no adapter specified";throw new h("There is no suitable adapter to dispatch the request "+s,"ERR_NOT_SUPPORT")}return i},adapters:le};function oe(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new q(null,e)}function xe(e){return oe(e),e.headers=C.from(e.headers),e.data=re.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),Ye.getAdapter(e.adapter||$.adapter)(e).then(function(i){return oe(e),i.data=re.call(e,e.transformResponse,i),i.headers=C.from(i.headers),i},function(i){return Xe(i)||(oe(e),i&&i.response&&(i.response.data=re.call(e,e.transformResponse,i.response),i.response.headers=C.from(i.response.headers))),Promise.reject(i)})}const et="1.10.0",ne={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{ne[e]=function(i){return typeof i===e||"a"+(t<1?"n ":" ")+e}});const Ce={};ne.transitional=function(t,n,i){function r(o,s){return"[Axios v"+et+"] Transitional option '"+o+"'"+s+(i?". "+i:"")}return(o,s,d)=>{if(t===!1)throw new h(r(s," has been removed"+(n?" in "+n:"")),h.ERR_DEPRECATED);return n&&!Ce[s]&&(Ce[s]=!0,console.warn(r(s," has been deprecated since v"+n+" and will be removed in the near future"))),t?t(o,s,d):!0}};ne.spelling=function(t){return(n,i)=>(console.warn(`${i} is likely a misspelling of ${t}`),!0)};function kn(e,t,n){if(typeof e!="object")throw new h("options must be an object",h.ERR_BAD_OPTION_VALUE);const i=Object.keys(e);let r=i.length;for(;r-- >0;){const o=i[r],s=t[o];if(s){const d=e[o],u=d===void 0||s(d,o,e);if(u!==!0)throw new h("option "+o+" must be "+u,h.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new h("Unknown option "+o,h.ERR_BAD_OPTION)}}const X={assertOptions:kn,validators:ne},k=X.validators;let U=class{constructor(t){this.defaults=t||{},this.interceptors={request:new Ee,response:new Ee}}async request(t,n){try{return await this._request(t,n)}catch(i){if(i instanceof Error){let r={};Error.captureStackTrace?Error.captureStackTrace(r):r=new Error;const o=r.stack?r.stack.replace(/^.+\n/,""):"";try{i.stack?o&&!String(i.stack).endsWith(o.replace(/^.+\n.+\n/,""))&&(i.stack+=` -`+o):i.stack=o}catch{}}throw i}}_request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=D(this.defaults,n);const{transitional:i,paramsSerializer:r,headers:o}=n;i!==void 0&&X.assertOptions(i,{silentJSONParsing:k.transitional(k.boolean),forcedJSONParsing:k.transitional(k.boolean),clarifyTimeoutError:k.transitional(k.boolean)},!1),r!=null&&(a.isFunction(r)?n.paramsSerializer={serialize:r}:X.assertOptions(r,{encode:k.function,serialize:k.function},!0)),n.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?n.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:n.allowAbsoluteUrls=!0),X.assertOptions(n,{baseUrl:k.spelling("baseURL"),withXsrfToken:k.spelling("withXSRFToken")},!0),n.method=(n.method||this.defaults.method||"get").toLowerCase();let s=o&&a.merge(o.common,o[n.method]);o&&a.forEach(["delete","get","head","post","put","patch","common"],f=>{delete o[f]}),n.headers=C.concat(s,o);const d=[];let u=!0;this.interceptors.request.forEach(function(m){typeof m.runWhen=="function"&&m.runWhen(n)===!1||(u=u&&m.synchronous,d.unshift(m.fulfilled,m.rejected))});const c=[];this.interceptors.response.forEach(function(m){c.push(m.fulfilled,m.rejected)});let l,p=0,y;if(!u){const f=[xe.bind(this),void 0];for(f.unshift.apply(f,d),f.push.apply(f,c),y=f.length,l=Promise.resolve(n);p{if(!i._listeners)return;let o=i._listeners.length;for(;o-- >0;)i._listeners[o](r);i._listeners=null}),this.promise.then=r=>{let o;const s=new Promise(d=>{i.subscribe(d),o=d}).then(r);return s.cancel=function(){i.unsubscribe(o)},s},t(function(o,s,d){i.reason||(i.reason=new q(o,s,d),n(i.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexOf(t);n!==-1&&this._listeners.splice(n,1)}toAbortSignal(){const t=new AbortController,n=i=>{t.abort(i)};return this.subscribe(n),t.signal.unsubscribe=()=>this.unsubscribe(n),t.signal}static source(){let t;return{token:new tt(function(r){t=r}),cancel:t}}};function Bn(e){return function(n){return e.apply(null,n)}}function Ln(e){return a.isObject(e)&&e.isAxiosError===!0}const ue={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(ue).forEach(([e,t])=>{ue[t]=e});function nt(e){const t=new U(e),n=Ne(U.prototype.request,t);return a.extend(n,U.prototype,t,{allOwnKeys:!0}),a.extend(n,t,null,{allOwnKeys:!0}),n.create=function(r){return nt(D(e,r))},n}const g=nt($);g.Axios=U;g.CanceledError=q;g.CancelToken=Nn;g.isCancel=Xe;g.VERSION=et;g.toFormData=ee;g.AxiosError=h;g.Cancel=g.CanceledError;g.all=function(t){return Promise.all(t)};g.spread=Bn;g.isAxiosError=Ln;g.mergeConfig=D;g.AxiosHeaders=C;g.formToJSON=e=>Ve(a.isHTMLForm(e)?new FormData(e):e);g.getAdapter=Ye.getAdapter;g.HttpStatusCode=ue;g.default=g;const{Axios:Yn,AxiosError:ei,CanceledError:ti,isCancel:ni,CancelToken:ii,VERSION:ri,all:oi,Cancel:si,isAxiosError:ai,spread:di,toFormData:ci,AxiosHeaders:li,HttpStatusCode:ui,formToJSON:fi,getAdapter:pi,mergeConfig:vi}=g,he=g.create({baseURL:"http://localhost:4000/api/v1",headers:{"Content-Type":"application/json"}});class Fn{async sync(t){try{const{data:n}=await he.post("/products/sync",{items:t});return console.log("[NestJS] Response (bulk):",n),n}catch(n){throw console.error("[NestJS] Error (bulk):",n),n}}}const Un=new Fn;function b(e){return new Promise(t=>setTimeout(t,e))}class Dn{base64ToFile(t,n,i){const r=t.includes(",")?t.split(",")[1]:t,o=atob(r),s=new ArrayBuffer(o.length),d=new Uint8Array(s);for(let c=0;c{let s=0,d=!1;const u=()=>{const c=document.evaluate(d&&r?r:t,document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;if(c instanceof HTMLElement){o(c);return}s++,s{const s=new FileReader;s.onloadend=()=>{typeof s.result=="string"?r(s.result.split(",")[1]):o("Không thể đọc dữ liệu ảnh")},s.onerror=o,s.readAsDataURL(i)})}getImageExtension(t){try{const i=new URL(t).pathname.match(/\.([a-zA-Z0-9]+)$/);return i?i[1].toLowerCase():null}catch{const r=t.split("?")[0].match(/\.([a-zA-Z0-9]+)$/);return r?r[1].toLowerCase():null}}imageLocalToBase64(t){return new Promise((n,i)=>{try{const r=chrome.runtime.getURL(`${t}`);fetch(r).then(o=>o.blob()).then(o=>{const s=new FileReader;s.onloadend=()=>n(s.result),s.onerror=i,s.readAsDataURL(o)}).catch(i)}catch(r){i(r)}})}scrollToElement(t,n="smooth"){t&&t.scrollIntoView({behavior:n,block:"center",inline:"nearest"})}getElementPointCoores(t){if(!t)return null;const n=t.getBoundingClientRect(),i=n.left+n.width/2,r=n.top+n.height/2;return{x:i,y:r}}setInputValue(t,n){t&&(t.value=n,t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}writeToInput=async(t,n,i)=>{const r=await this.getElementByXPath(n,{xpathFallback:i});if(!r)throw new Error("Xpath is not found with value: "+t);this.scrollToElement(r),this.clickByPoint(r),this.setInputValue(r,t)};pressEnter(t){if(!t)throw new Error("Textarea not found:",t);t.focus(),["keydown","keypress","keyup"].forEach(n=>{t.dispatchEvent(new KeyboardEvent(n,{key:"Enter",code:"Enter",keyCode:13,which:13,bubbles:!0,cancelable:!0}))})}}const w=new Dn,A={file__image_input:'input[type="file"]',title_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[2]/div[1]/div[2]/div/div/div[5]/div/div/div/label/div/input",price_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[6]/div/div/div/label/div/input",brand_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[2]/div/div/div/label/div/input",brand_input_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[2]/div/div/div/label/div/input",description_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[3]/div/div/div/label/div/div/textarea",description_input_falback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[3]/div/div/div/label/div/div/textarea",sku_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[6]/div/div/div[1]/label/div/input",sku_input_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[6]/div/div/div[1]/label/div/input",category_select:{wraper:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[7]/div/div/div/div",container:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/div/div/div/div/span/div"},condition_select:{wraper:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[8]/div/div/div/div",container:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/div/div/div/div[1]/div"},tags_input:{input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[5]/div/div/div/div[1]/label/div/div/div[2]/div/textarea",input_falback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[5]/div/div/div/div[1]/label/div/div/div[2]/div/textarea"},location_select:{input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[7]/div/div/div/div/div/div/div/div/label/div[2]/input",input_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[7]/div/div/div/div/div/div/div/div/label/div[2]/input",wraper:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[7]/div/div/div/div/div/div/div/div",container:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/ul",container_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/ul"},next_btn:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[5]/div/div/div",publish_btn:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[4]/div[2]/div/div",products:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[2]/div/div/div[2]/div[1]/div/div[2]/div/div/span/div/div",products_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[2]/div/div/div[2]/div[1]/div/div[2]/div[2]/div"},jn=async e=>{const t=new DataTransfer;for(const i of e.images){const r=await w.imageUrlToBase64(i);console.log("Base64:",i.slice(0,50)+"...");const o=w.base64ToFile(r,e.sku,w.getImageExtension(i)||"jpg");t.items.add(o)}const n=document.querySelector(A.file__image_input);n?(n.files=t.files,n.dispatchEvent(new Event("change",{bubbles:!0}))):console.error("Không tìm thấy input[type='file']")},Pe=async(e,t)=>{const n=await w.getElementByXPath(t.wraper);if(!n)throw new Error("Wrapper xpath not found");w.scrollToElement(n),w.clickByPoint(n),await b(200);const i=await w.getElementByXPath(t.container);if(!i)throw new Error("Container xpath not found");const r=Array.from(i.children).find(o=>o.textContent?.trim().toLocaleLowerCase().replace(/–/g,"-").includes(e.toLocaleLowerCase()));if(!r)throw new Error(`No child found with text "${e}"`);w.scrollToElement(r),await b(200),w.clickByPoint(r)},qn=async(e,{input:t,...n})=>{await w.writeToInput(e,t,n.input_fallback),await b(200);const i=await w.getElementByXPath(n.container,{xpathFallback:n.container_fallback});if(!i)throw new Error("Container xpath not found");w.scrollToElement(i);const r=Array.from(i.children).find(o=>o.textContent?.trim().toLocaleLowerCase().includes(e.toLocaleLowerCase()));if(!r)throw new Error(`No child found with text "${e}"`);w.scrollToElement(r),await b(200),w.clickByPoint(r)},In=async(e,t)=>{const n=await w.getElementByXPath(t.input,{xpathFallback:t?.input_falback});if(!n)throw new Error("Input is not found");w.scrollToElement(n),await b(200);for(const i of e)await w.writeToInput(i,t.input,t?.input_falback),await b(200),w.pressEnter(n)},ke=async(e,t)=>{const{data:n}=await he({url:"products/publist-finish/"+e.id,method:"POST",data:t});return n},Mn=async(e,t)=>{const{data:n}=await he({url:"products/delete-finish/"+e.id,method:"POST",data:t});return n},Hn=async()=>{const e=await w.getElementByXPath(A.next_btn);if(!e)throw new Error("Next button is not found");w.clickByPoint(e)},$n=async()=>{const e=await w.getElementByXPath(A.publish_btn);if(!e)throw new Error("Publist button is not found");w.clickByPoint(e)};function zn(e){return Array.from(e.children).map(n=>{const r=n.querySelector('span[dir="auto"], div[dir="auto"]')?.textContent?.trim()||"",s=Array.from(n.querySelectorAll('span[dir="auto"]')).find(u=>/\d/.test(u.textContent||"")&&/[AU$]/.test(u.textContent||""))?.textContent?.match(/\d+(?:\.\d+)?/),d=s?parseFloat(s[0]):0;return{title:r,price:d,el:e}})}const Jn=async e=>(console.log({item:e}),await b(1e3),await jn(e),await b(200),w.writeToInput(e.title,A.title_input),await b(200),w.writeToInput(String(e.price),A.price_input),await b(200),await Pe(e.category,A.category_select),await b(200),await Pe(e.condition,A.condition_select),e.brand&&(await b(200),await w.writeToInput(e.brand,A.brand_input,A.brand_input_fallback)),await b(200),await w.writeToInput(e.description,A.description_input,A.description_input_falback),await b(200),await In(e.tags,A.tags_input),await b(200),await w.writeToInput(e.sku,A.sku_input,A.sku_input_fallback),e?.location&&(await b(200),await qn(e.location,A.location_select)),await b(200),await Hn(),await b(200),await $n(),!0),it=async()=>{let e=await w.getElementByXPath(A.products);return e||(e=await w.getElementByXPath(A.products_fallback)),e?zn(e):[]},Vn=async()=>{if(!window.location.href.includes("https://www.facebook.com/marketplace/you/selling"))return;const t=await it();if(!t.length)return;const n=await Un.sync(t.map(i=>({title:i.title,price:i.price})));console.log({response:n})},Xn=async e=>{chrome.runtime.sendMessage({type:"close-tab",payload:e})},Kn=async e=>{const t=await it(),n=t.find(c=>c.title==e.title&&c.price==e.price);if(console.log({payload:e,product:n,products:t}),!n)return;const r=n.el.querySelector(`[aria-label="More options for ${n.title}"]`);if(console.log({optionEl:r}),!r)return;r.click?.(),await b(2e3);const o=Array.from(document.querySelectorAll('[role="menuitem"]'));console.log({items:o}),o.find(c=>c.textContent.toLocaleLowerCase().includes("delete")).click?.(),await b(1e3);const d=await w.getElementByXPath("/html/body/div[1]/div/div[1]/div/div[4]/div/div/div[1]/div/div[2]/div/div/div/div/div/div/div[3]/div/div/div/div/div[1]/div",{xpathFallback:"/html/body/div[1]/div/div[1]/div/div[4]/div/div/div[1]/div/div[2]/div/div/div/div[3]/div[2]/div/div[2]/div[1]"});console.log({btnDelete:d}),d?.click(),(await w.getElementByXPath("/html/body/div[1]/div/div[1]/div/div[4]/div/div/div[1]/div/div[2]/div/div/div/div[2]/div"))?.click(),await Mn(e,{published:!1}),chrome.runtime.sendMessage({type:"delete-done"})},Wn=chrome.runtime.connect();Wn.onMessage.addListener(async e=>{if(e.type==="publist-event"){const t=e.payload;if(!t)return;console.log("Received new product event:",t);try{await b(500),await Jn(t)}catch(n){await ke(t,{error:n.message,published:!1})}finally{await ke(t,{published:!0}),await b(5e3),await Xn(t)}}});chrome.runtime.onMessage.addListener(e=>{e.type==="DELETE_STREAM_DATA"&&(console.log("Nhận dữ liệu từ background:",e.payload),Kn(e.payload))});async function Gn(){await Vn()}Gn(); +`+o):i.stack=o}catch{}}throw i}}_request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=D(this.defaults,n);const{transitional:i,paramsSerializer:r,headers:o}=n;i!==void 0&&X.assertOptions(i,{silentJSONParsing:k.transitional(k.boolean),forcedJSONParsing:k.transitional(k.boolean),clarifyTimeoutError:k.transitional(k.boolean)},!1),r!=null&&(a.isFunction(r)?n.paramsSerializer={serialize:r}:X.assertOptions(r,{encode:k.function,serialize:k.function},!0)),n.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?n.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:n.allowAbsoluteUrls=!0),X.assertOptions(n,{baseUrl:k.spelling("baseURL"),withXsrfToken:k.spelling("withXSRFToken")},!0),n.method=(n.method||this.defaults.method||"get").toLowerCase();let s=o&&a.merge(o.common,o[n.method]);o&&a.forEach(["delete","get","head","post","put","patch","common"],f=>{delete o[f]}),n.headers=C.concat(s,o);const d=[];let u=!0;this.interceptors.request.forEach(function(m){typeof m.runWhen=="function"&&m.runWhen(n)===!1||(u=u&&m.synchronous,d.unshift(m.fulfilled,m.rejected))});const c=[];this.interceptors.response.forEach(function(m){c.push(m.fulfilled,m.rejected)});let l,p=0,y;if(!u){const f=[xe.bind(this),void 0];for(f.unshift.apply(f,d),f.push.apply(f,c),y=f.length,l=Promise.resolve(n);p{if(!i._listeners)return;let o=i._listeners.length;for(;o-- >0;)i._listeners[o](r);i._listeners=null}),this.promise.then=r=>{let o;const s=new Promise(d=>{i.subscribe(d),o=d}).then(r);return s.cancel=function(){i.unsubscribe(o)},s},t(function(o,s,d){i.reason||(i.reason=new q(o,s,d),n(i.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexOf(t);n!==-1&&this._listeners.splice(n,1)}toAbortSignal(){const t=new AbortController,n=i=>{t.abort(i)};return this.subscribe(n),t.signal.unsubscribe=()=>this.unsubscribe(n),t.signal}static source(){let t;return{token:new tt(function(r){t=r}),cancel:t}}};function Bn(e){return function(n){return e.apply(null,n)}}function Ln(e){return a.isObject(e)&&e.isAxiosError===!0}const ue={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(ue).forEach(([e,t])=>{ue[t]=e});function nt(e){const t=new U(e),n=Ne(U.prototype.request,t);return a.extend(n,U.prototype,t,{allOwnKeys:!0}),a.extend(n,t,null,{allOwnKeys:!0}),n.create=function(r){return nt(D(e,r))},n}const g=nt($);g.Axios=U;g.CanceledError=q;g.CancelToken=Nn;g.isCancel=Xe;g.VERSION=et;g.toFormData=ee;g.AxiosError=h;g.Cancel=g.CanceledError;g.all=function(t){return Promise.all(t)};g.spread=Bn;g.isAxiosError=Ln;g.mergeConfig=D;g.AxiosHeaders=C;g.formToJSON=e=>Ve(a.isHTMLForm(e)?new FormData(e):e);g.getAdapter=Ye.getAdapter;g.HttpStatusCode=ue;g.default=g;const{Axios:Yn,AxiosError:ei,CanceledError:ti,isCancel:ni,CancelToken:ii,VERSION:ri,all:oi,Cancel:si,isAxiosError:ai,spread:di,toFormData:ci,AxiosHeaders:li,HttpStatusCode:ui,formToJSON:fi,getAdapter:pi,mergeConfig:vi}=g,he=g.create({baseURL:"http://localhost:4000/api/v1",headers:{"Content-Type":"application/json"}});class Fn{async sync(t){try{const{data:n}=await he.post("/products/sync",{items:t});return console.log("[NestJS] Response (bulk):",n),n}catch(n){throw console.error("[NestJS] Error (bulk):",n),n}}}const Un=new Fn;function b(e){return new Promise(t=>setTimeout(t,e))}class Dn{base64ToFile(t,n,i){const r=t.includes(",")?t.split(",")[1]:t,o=atob(r),s=new ArrayBuffer(o.length),d=new Uint8Array(s);for(let c=0;c{let s=0,d=!1;const u=()=>{const c=document.evaluate(d&&r?r:t,document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;if(c instanceof HTMLElement){o(c);return}s++,s{const s=new FileReader;s.onloadend=()=>{typeof s.result=="string"?r(s.result.split(",")[1]):o("Không thể đọc dữ liệu ảnh")},s.onerror=o,s.readAsDataURL(i)})}getImageExtension(t){try{const i=new URL(t).pathname.match(/\.([a-zA-Z0-9]+)$/);return i?i[1].toLowerCase():null}catch{const r=t.split("?")[0].match(/\.([a-zA-Z0-9]+)$/);return r?r[1].toLowerCase():null}}imageLocalToBase64(t){return new Promise((n,i)=>{try{const r=chrome.runtime.getURL(`${t}`);fetch(r).then(o=>o.blob()).then(o=>{const s=new FileReader;s.onloadend=()=>n(s.result),s.onerror=i,s.readAsDataURL(o)}).catch(i)}catch(r){i(r)}})}scrollToElement(t,n="smooth"){t&&t.scrollIntoView({behavior:n,block:"center",inline:"nearest"})}getElementPointCoores(t){if(!t)return null;const n=t.getBoundingClientRect(),i=n.left+n.width/2,r=n.top+n.height/2;return{x:i,y:r}}setInputValue(t,n){t&&(t.value=n,t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}writeToInput=async(t,n,i)=>{const r=await this.getElementByXPath(n,{xpathFallback:i});if(!r)throw new Error("Xpath is not found with value: "+t);this.scrollToElement(r),this.clickByPoint(r),this.setInputValue(r,t)};pressEnter(t){if(!t)throw new Error("Textarea not found:",t);t.focus(),["keydown","keypress","keyup"].forEach(n=>{t.dispatchEvent(new KeyboardEvent(n,{key:"Enter",code:"Enter",keyCode:13,which:13,bubbles:!0,cancelable:!0}))})}}const w=new Dn,A={file__image_input:'input[type="file"]',title_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[2]/div[1]/div[2]/div/div/div[5]/div/div/div/label/div/input",price_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[6]/div/div/div/label/div/input",brand_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[2]/div/div/div/label/div/input",brand_input_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[2]/div/div/div/label/div/input",description_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[3]/div/div/div/label/div/div/textarea",description_input_falback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[3]/div/div/div/label/div/div/textarea",sku_input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[6]/div/div/div[1]/label/div/input",sku_input_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[6]/div/div/div[1]/label/div/input",category_select:{wraper:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[7]/div/div/div/div",container:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/div/div/div/div/span/div"},condition_select:{wraper:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[8]/div/div/div/div",container:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/div/div/div/div[1]/div"},tags_input:{input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[5]/div/div/div/div[1]/label/div/div/div[2]/div/textarea",input_falback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[5]/div/div/div/div[1]/label/div/div/div[2]/div/textarea"},location_select:{input:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[7]/div/div/div/div/div/div/div/div/label/div[2]/input",input_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[10]/div/div/div[7]/div/div/div/div/div/div/div/div/label/div[2]/input",wraper:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div[2]/div/div/div[9]/div/div/div[7]/div/div/div/div/div/div/div/div",container:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/ul",container_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[2]/div/div/div[1]/div[1]/div/ul"},next_btn:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[5]/div/div/div",publish_btn:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[1]/div/div[4]/div[2]/div/div",products:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[2]/div/div/div[2]/div[1]/div/div[2]/div/div/span/div/div",products_fallback:"/html/body/div[1]/div/div[1]/div/div[3]/div/div/div[1]/div[1]/div[2]/div/div/div[2]/div[1]/div/div[2]/div[2]/div"},jn=async e=>{const t=new DataTransfer;for(const i of e.images){const r=await w.imageUrlToBase64(i);console.log("Base64:",i.slice(0,50)+"...");const o=w.base64ToFile(r,e.sku,w.getImageExtension(i)||"jpg");t.items.add(o)}const n=document.querySelector(A.file__image_input);n?(n.files=t.files,n.dispatchEvent(new Event("change",{bubbles:!0}))):console.error("Không tìm thấy input[type='file']")},Pe=async(e,t)=>{const n=await w.getElementByXPath(t.wraper);if(!n)throw new Error("Wrapper xpath not found");w.scrollToElement(n),w.clickByPoint(n),await b(200);const i=await w.getElementByXPath(t.container);if(!i)throw new Error("Container xpath not found");const r=Array.from(i.children).find(o=>o.textContent?.trim().toLocaleLowerCase().replace(/–/g,"-").includes(e.toLocaleLowerCase()));if(!r)throw new Error(`No child found with text "${e}"`);w.scrollToElement(r),await b(200),w.clickByPoint(r)},qn=async(e,{input:t,...n})=>{await w.writeToInput(e,t,n.input_fallback),await b(200);const i=await w.getElementByXPath(n.container,{xpathFallback:n.container_fallback});if(!i)throw new Error("Container xpath not found");w.scrollToElement(i);const r=Array.from(i.children).find(o=>o.textContent?.trim().toLocaleLowerCase().includes(e.toLocaleLowerCase()));if(!r)throw new Error(`No child found with text "${e}"`);w.scrollToElement(r),await b(200),w.clickByPoint(r)},In=async(e,t)=>{const n=await w.getElementByXPath(t.input,{xpathFallback:t?.input_falback});if(!n)throw new Error("Input is not found");w.scrollToElement(n),await b(200);for(const i of e)await w.writeToInput(i,t.input,t?.input_falback),await b(200),w.pressEnter(n)},ke=async(e,t)=>{const{data:n}=await he({url:"products/publist-finish/"+e.id,method:"POST",data:t});return n},Mn=async(e,t)=>{const{data:n}=await he({url:"products/delete-finish/"+e.id,method:"POST",data:t});return n},Hn=async()=>{const e=await w.getElementByXPath(A.next_btn);if(!e)throw new Error("Next button is not found");w.clickByPoint(e)},$n=async()=>{const e=await w.getElementByXPath(A.publish_btn);if(!e)throw new Error("Publist button is not found");w.clickByPoint(e)},zn=async e=>(console.log({item:e}),await b(1e3),await jn(e),await b(200),w.writeToInput(e.title,A.title_input),await b(200),w.writeToInput(String(e.price),A.price_input),await b(200),await Pe(e.category,A.category_select),await b(200),await Pe(e.condition,A.condition_select),e.brand&&(await b(200),await w.writeToInput(e.brand,A.brand_input,A.brand_input_fallback)),await b(200),await w.writeToInput(e.description,A.description_input,A.description_input_falback),await b(200),await In(e.tags,A.tags_input),await b(200),await w.writeToInput(e.sku,A.sku_input,A.sku_input_fallback),e?.location&&(await b(200),await qn(e.location,A.location_select)),await b(200),await Hn(),await b(200),await $n(),!0),it=async()=>{const e=await w.getElementByXPath(A.products),t=await w.getElementByXPath(A.products_fallback),n=[e,t].filter(Boolean);return n.length===0?[]:n.flatMap(i=>Jn(i))};function Jn(e){return Array.from(e.children).map(n=>{const r=n.querySelector('span[dir="auto"], div[dir="auto"]')?.textContent?.trim()||"",s=Array.from(n.querySelectorAll('span[dir="auto"]')).find(u=>/\d/.test(u.textContent||"")&&/[AU$]/.test(u.textContent||""))?.textContent?.match(/\d+(?:\.\d+)?/),d=s?parseFloat(s[0]):0;return{title:r,price:d,el:e}})}const Vn=async()=>{if(!window.location.href.includes("https://www.facebook.com/marketplace/you/selling"))return;const t=await it();if(!t.length)return;const n=await Un.sync(t.map(i=>({title:i.title,price:i.price})));console.log({response:n})},Xn=async e=>{chrome.runtime.sendMessage({type:"close-tab",payload:e})},Kn=async e=>{const t=await it(),n=t.find(c=>c.title==e.title&&c.price==e.price);if(console.log({payload:e,product:n,products:t}),!n)return;const r=n.el.querySelector(`[aria-label="More options for ${n.title}"]`);if(console.log({optionEl:r}),!r)return;r.click?.(),await b(2e3);const o=Array.from(document.querySelectorAll('[role="menuitem"]'));console.log({items:o}),o.find(c=>c.textContent.toLocaleLowerCase().includes("delete")).click?.(),await b(1e3);const d=await w.getElementByXPath("/html/body/div[1]/div/div[1]/div/div[4]/div/div/div[1]/div/div[2]/div/div/div/div/div/div/div[3]/div/div/div/div/div[1]/div",{xpathFallback:"/html/body/div[1]/div/div[1]/div/div[4]/div/div/div[1]/div/div[2]/div/div/div/div[3]/div[2]/div/div[2]/div[1]"});console.log({btnDelete:d}),d?.click(),(await w.getElementByXPath("/html/body/div[1]/div/div[1]/div/div[4]/div/div/div[1]/div/div[2]/div/div/div/div[2]/div"))?.click(),await Mn(e,{published:!1}),chrome.runtime.sendMessage({type:"delete-done"})},Wn=chrome.runtime.connect();Wn.onMessage.addListener(async e=>{if(e.type==="publist-event"){const t=e.payload;if(!t)return;console.log("Received new product event:",t);try{await b(500),await zn(t)}catch(n){await ke(t,{error:n.message,published:!1})}finally{await ke(t,{published:!0}),await b(5e3),await Xn(t)}}});chrome.runtime.onMessage.addListener(e=>{e.type==="DELETE_STREAM_DATA"&&(console.log("Nhận dữ liệu từ background:",e.payload),Kn(e.payload))});async function Gn(){await Vn()}Gn(); diff --git a/auto-listing-facebook-marketplace/src/content.ts b/auto-listing-facebook-marketplace/src/content.ts index 68ee4db..2c92c37 100644 --- a/auto-listing-facebook-marketplace/src/content.ts +++ b/auto-listing-facebook-marketplace/src/content.ts @@ -233,28 +233,6 @@ const clickPublist = async () => { thiefService.clickByPoint(btn); }; -function extractListings(productsEl: HTMLElement) { - const children = Array.from(productsEl.children); - - return children.map((child) => { - // Lấy title (thường là span hoặc div có dir="auto") - const titleEl = child.querySelector('span[dir="auto"], div[dir="auto"]'); - const title = titleEl?.textContent?.trim() || ""; - - // Lấy giá (span có attribute dir="auto") - const priceEl = Array.from(child.querySelectorAll('span[dir="auto"]')).find( - (el) => - /\d/.test(el.textContent || "") && /[AU$]/.test(el.textContent || "") - ); - - // Tách lấy số, ví dụ: "AU$20" -> "20" - const priceMatch = priceEl?.textContent?.match(/\d+(?:\.\d+)?/); - const price = priceMatch ? parseFloat(priceMatch[0]) : 0; - - return { title, price, el: productsEl }; - }); -} - /** * B1. Upload images * B2. Write title @@ -333,19 +311,79 @@ const handle = async (item: IItem) => { return true; }; +// function extractListings(productsEl: HTMLElement) { +// const children = Array.from(productsEl.children); + +// return children.map((child) => { +// // Lấy title (thường là span hoặc div có dir="auto") +// const titleEl = child.querySelector('span[dir="auto"], div[dir="auto"]'); +// const title = titleEl?.textContent?.trim() || ""; + +// // Lấy giá (span có attribute dir="auto") +// const priceEl = Array.from(child.querySelectorAll('span[dir="auto"]')).find( +// (el) => +// /\d/.test(el.textContent || "") && /[AU$]/.test(el.textContent || "") +// ); + +// // Tách lấy số, ví dụ: "AU$20" -> "20" +// const priceMatch = priceEl?.textContent?.match(/\d+(?:\.\d+)?/); +// const price = priceMatch ? parseFloat(priceMatch[0]) : 0; + +// return { title, price, el: productsEl }; +// }); +// } + +// const getProducts = async () => { +// let products = await thiefService.getElementByXPath(selectors.products); +// if (!products) { +// products = await thiefService.getElementByXPath( +// selectors.products_fallback +// ); +// } + +// if (!products) return []; + +// return extractListings(products) as ISyncItem[]; +// }; + const getProducts = async () => { - let products = await thiefService.getElementByXPath(selectors.products); - if (!products) { - products = await thiefService.getElementByXPath( - selectors.products_fallback - ); - } + const products1 = await thiefService.getElementByXPath(selectors.products); + const products2 = await thiefService.getElementByXPath( + selectors.products_fallback + ); - if (!products) return []; + // Gom 2 cái vào một mảng, bỏ null + const allProductsEls = [products1, products2].filter( + Boolean + ) as HTMLElement[]; - return extractListings(products) as ISyncItem[]; + if (allProductsEls.length === 0) return []; + + // Nối tất cả kết quả extractListings từ mỗi element + return allProductsEls.flatMap((el) => extractListings(el)) as ISyncItem[]; }; +function extractListings(productsEl: HTMLElement) { + const children = Array.from(productsEl.children); + + return children.map((child) => { + // Lấy title + const titleEl = child.querySelector('span[dir="auto"], div[dir="auto"]'); + const title = titleEl?.textContent?.trim() || ""; + + // Lấy giá + const priceEl = Array.from(child.querySelectorAll('span[dir="auto"]')).find( + (el) => + /\d/.test(el.textContent || "") && /[AU$]/.test(el.textContent || "") + ); + + const priceMatch = priceEl?.textContent?.match(/\d+(?:\.\d+)?/); + const price = priceMatch ? parseFloat(priceMatch[0]) : 0; + + return { title, price, el: productsEl }; + }); +} + const syncListing = async () => { const url = window.location.href; if (!url.includes("https://www.facebook.com/marketplace/you/selling")) return; diff --git a/client/app/components/core/data-table.tsx b/client/app/components/core/data-table.tsx index 7878974..46f1400 100644 --- a/client/app/components/core/data-table.tsx +++ b/client/app/components/core/data-table.tsx @@ -158,7 +158,7 @@ export interface Column { // Thêm interface cho custom actions interface CustomAction { key: string; - label: string; + label: string | ((row: T) => ReactNode); icon?: React.ReactNode; variant?: "default" | "secondary" | "destructive" | "outline"; action: (row: T) => void; @@ -1470,7 +1470,9 @@ export function DataTable>({ }`} > {action.icon && {action.icon}} - {action.label} + {typeof action.label === "string" + ? action.label + : action.label(row)} ))} diff --git a/client/app/components/nav-projects.tsx b/client/app/components/nav-projects.tsx index f18482e..4718fad 100644 --- a/client/app/components/nav-projects.tsx +++ b/client/app/components/nav-projects.tsx @@ -4,7 +4,7 @@ import { MoreHorizontal, Trash2, type LucideIcon, -} from "lucide-react" +} from "lucide-react"; import { DropdownMenu, @@ -12,7 +12,7 @@ import { DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, -} from "~/components/ui/dropdown-menu" +} from "~/components/ui/dropdown-menu"; import { SidebarGroup, SidebarGroupLabel, @@ -21,18 +21,18 @@ import { SidebarMenuButton, SidebarMenuItem, useSidebar, -} from "~/components/ui/sidebar" +} from "~/components/ui/sidebar"; export function NavProjects({ projects, }: { projects: { - name: string - url: string - icon: LucideIcon - }[] + name: string; + url: string; + icon: LucideIcon; + }[]; }) { - const { isMobile } = useSidebar() + const { isMobile } = useSidebar(); return ( @@ -83,5 +83,5 @@ export function NavProjects({ - ) + ); } diff --git a/client/app/layouts/private-layout.tsx b/client/app/layouts/private-layout.tsx index 8c70ddf..01cc1a1 100644 --- a/client/app/layouts/private-layout.tsx +++ b/client/app/layouts/private-layout.tsx @@ -1,3 +1,4 @@ +import { useEffect } from "react"; import { Outlet } from "react-router"; import { AppSidebar } from "~/components/app-sidebar"; @@ -14,6 +15,7 @@ import { SidebarInset, SidebarProvider, SidebarTrigger, + useSidebar, } from "~/components/ui/sidebar"; export default function PrivateLayout() { @@ -45,8 +47,9 @@ export default function PrivateLayout() { // // // ); + return ( - +
diff --git a/client/app/routes/products/list.tsx b/client/app/routes/products/list.tsx index 516ede3..3120e4e 100644 --- a/client/app/routes/products/list.tsx +++ b/client/app/routes/products/list.tsx @@ -208,8 +208,10 @@ export default function List() { // 🎯 Custom Actions - Đa dạng và có điều kiện const customActions = [ { - key: "publist", - label: "Publist", + key: "listing", + label: (row: IProduct) => { + return row?.status ? "Unlist" : "Publist"; + }, icon: , action: (data: IProduct) => { handlePublist(data); @@ -259,11 +261,15 @@ export default function List() { enabled: !!states, }); - const publistMutation = useMutation({ + const listingMutation = useMutation({ mutationFn: async (data: Partial) => { await delay(300); - return productApi.customAction(data.id || 0, "publist", data); + if (data.status) { + return productApi.customAction(data.id || 0, "unlist", data); + } else { + return productApi.customAction(data.id || 0, "publist", data); + } }, onSuccess: (data) => { console.log({ data }); @@ -273,7 +279,7 @@ export default function List() { const handlePublist = async (data: Partial) => { toast.promise( - publistMutation.mutateAsync(data), // gọi function để trả về promise + listingMutation.mutateAsync(data), // gọi function để trả về promise { loading: "Loading...", success: (result) => `${data?.title} toast has been added`, diff --git a/server/src/modules/products/products.controller.ts b/server/src/modules/products/products.controller.ts index 9537bfc..2097d1e 100644 --- a/server/src/modules/products/products.controller.ts +++ b/server/src/modules/products/products.controller.ts @@ -30,6 +30,11 @@ export class ProductsController extends CoreController< return this.service.publist(id); } + @Post('unlist/:id') + async unlist(@Param('id') id: Product['id']): Promise { + return this.service.unlist(id); + } + @Post('publist-finish/:id') async publistFinish( @Param('id') id: Product['id'], diff --git a/server/src/modules/products/products.service.ts b/server/src/modules/products/products.service.ts index d48fb2f..6595078 100644 --- a/server/src/modules/products/products.service.ts +++ b/server/src/modules/products/products.service.ts @@ -74,28 +74,28 @@ export class ProductsService extends CoreService { } } - protected async beforeDelete( - id: number, - data: Partial, - req: Request, - ): Promise { - // Emit sự kiện để bắt đầu publish - this.eventService.sendEvent(ProductsService.EVENTS.SEND_DELETE, { - ...data, - }); + // protected async beforeDelete( + // id: number, + // data: Partial, + // req: Request, + // ): Promise { + // // Emit sự kiện để bắt đầu publish + // this.eventService.sendEvent(ProductsService.EVENTS.SEND_DELETE, { + // ...data, + // }); - // Đợi phản hồi từ client - const result = await this.waitForDeleteResult({ ...data, id }); + // // Đợi phản hồi từ client + // const result = await this.waitForDeleteResult({ ...data, id }); - if (!result) - throw new BadRequestException( - AppResponse.toResponse(false, { - message: SystemLang.getText('messages', 'try_again'), - }), - ); + // if (!result) + // throw new BadRequestException( + // AppResponse.toResponse(false, { + // message: SystemLang.getText('messages', 'try_again'), + // }), + // ); - return true; - } + // return true; + // } async create(data: CreateProductDto): Promise { const product = this.repo.create(data); @@ -197,6 +197,32 @@ export class ProductsService extends CoreService { return AppResponse.toResponse(plainData); } + async unlist(id: Product['id']) { + const product = await this.repo.findOne({ where: { id } }); + + if (!product) { + throw new NotFoundException( + AppResponse.toResponse(null, { + message: SystemLang.getText('messages', 'not_found'), + }), + ); + } + + const plainData = plainToClass(Product, product); + + // Emit sự kiện để bắt đầu publish + this.eventService.sendEvent(ProductsService.EVENTS.SEND_DELETE, { + ...plainData, + }); + + // Đợi phản hồi từ client + const publistResult = await this.waitForDeleteResult(plainData); + + await this.repo.update(plainData.id, { status: false }); + + return AppResponse.toResponse(plainData); + } + /** * Chờ event PUBLIST_FINISH và bắt lỗi nếu quá thời gian hoặc lỗi bất ngờ */