From d92936681042d72d58a06a32095ec160c0ef42a2 Mon Sep 17 00:00:00 2001 From: Admin Date: Tue, 20 May 2025 08:02:14 +0700 Subject: [PATCH 1/2] update view admin --- .DS_Store | Bin 8196 -> 6148 bytes auto-bid-admin/src/apis/scrap.ts | 47 ++ .../src/components/dashboard/working-page.tsx | 42 +- .../components/web-bid/scrap-config.modal.tsx | 163 ++++++ .../components/web-bid/web-account-modal.tsx | 207 ++++--- .../src/components/web-bid/web-bid-modal.tsx | 35 +- auto-bid-admin/src/pages/bids.tsx | 2 +- auto-bid-admin/src/pages/web-bids.tsx | 525 ++++++++++-------- auto-bid-admin/src/system/type/index.ts | 123 ++-- auto-bid-server/bot-data/metadata.json | 2 +- auto-bid-server/package-lock.json | 291 ++++++++++ auto-bid-server/package.json | 1 + auto-bid-server/src/app.module.ts | 2 + .../src/modules/bids/entities/bid.entity.ts | 3 + .../modules/bids/entities/wed-bid.entity.ts | 13 +- .../src/modules/bids/services/bids.service.ts | 18 +- .../modules/bids/services/tasks.servise.ts | 2 +- .../modules/bids/services/web-bids.service.ts | 8 + .../controllers/scrap-config.controller.ts | 60 ++ .../dto/scrap-config/create-scrap-config.ts | 13 + .../dto/scrap-config/update-scrap-config.ts | 4 + .../scraps/entities/scrap-config.entity.ts | 33 ++ .../scraps/entities/scrap-item.entity.ts | 39 ++ .../src/modules/scraps/entities/timestamp.ts | 8 + .../https:/www.grays.com/grays-scrap-model.ts | 71 +++ .../modules/scraps/models/scrap-interface.ts | 10 + .../src/modules/scraps/models/scrap-model.ts | 61 ++ .../src/modules/scraps/scraps.module.ts | 16 + .../scraps/services/scrap-config.service.ts | 60 ++ .../services/scrap-item-config.service.ts | 12 + .../modules/scraps/services/tasks.service.ts | 25 + auto-bid-server/src/ultils/index.ts | 135 ++--- auto-bid-tool/index.js | 35 +- .../models/allbids.com.au/allbids-api-bid.js | 175 ++++++ .../allbids.com.au/allbids-product-bid.js | 290 ++++++++++ .../models/grays.com/grays-product-bid.js | 7 + auto-bid-tool/note.md | 60 +- auto-bid-tool/service/app-service.js | 8 + auto-bid-tool/system/config.js | 5 + auto-bid-tool/system/utils.js | 40 ++ bid-extension.zip | Bin 0 -> 21484 bytes bid-extension/.DS_Store | Bin 6148 -> 0 bytes bid-extension/assets/css/index.css | 134 ----- bid-extension/assets/icons/128.png | Bin 4131 -> 0 bytes bid-extension/assets/icons/16.png | Bin 305 -> 0 bytes bid-extension/assets/icons/32.png | Bin 522 -> 0 bytes bid-extension/background.js | 4 - bid-extension/config.js | 6 - bid-extension/content.js | 394 ------------- bid-extension/manifest.json | 42 -- bid-extension/pages/popup/popup.html | 170 ------ bid-extension/pages/popup/popup.js | 17 - 52 files changed, 2127 insertions(+), 1291 deletions(-) create mode 100644 auto-bid-admin/src/apis/scrap.ts create mode 100644 auto-bid-admin/src/components/web-bid/scrap-config.modal.tsx create mode 100644 auto-bid-server/src/modules/scraps/controllers/scrap-config.controller.ts create mode 100644 auto-bid-server/src/modules/scraps/dto/scrap-config/create-scrap-config.ts create mode 100644 auto-bid-server/src/modules/scraps/dto/scrap-config/update-scrap-config.ts create mode 100644 auto-bid-server/src/modules/scraps/entities/scrap-config.entity.ts create mode 100644 auto-bid-server/src/modules/scraps/entities/scrap-item.entity.ts create mode 100644 auto-bid-server/src/modules/scraps/entities/timestamp.ts create mode 100644 auto-bid-server/src/modules/scraps/models/https:/www.grays.com/grays-scrap-model.ts create mode 100644 auto-bid-server/src/modules/scraps/models/scrap-interface.ts create mode 100644 auto-bid-server/src/modules/scraps/models/scrap-model.ts create mode 100644 auto-bid-server/src/modules/scraps/scraps.module.ts create mode 100644 auto-bid-server/src/modules/scraps/services/scrap-config.service.ts create mode 100644 auto-bid-server/src/modules/scraps/services/scrap-item-config.service.ts create mode 100644 auto-bid-server/src/modules/scraps/services/tasks.service.ts create mode 100644 auto-bid-tool/models/allbids.com.au/allbids-api-bid.js create mode 100644 auto-bid-tool/models/allbids.com.au/allbids-product-bid.js create mode 100644 bid-extension.zip delete mode 100644 bid-extension/.DS_Store delete mode 100644 bid-extension/assets/css/index.css delete mode 100644 bid-extension/assets/icons/128.png delete mode 100644 bid-extension/assets/icons/16.png delete mode 100644 bid-extension/assets/icons/32.png delete mode 100644 bid-extension/background.js delete mode 100644 bid-extension/config.js delete mode 100644 bid-extension/content.js delete mode 100644 bid-extension/manifest.json delete mode 100644 bid-extension/pages/popup/popup.html delete mode 100644 bid-extension/pages/popup/popup.js diff --git a/.DS_Store b/.DS_Store index dfa9dfe442ac880f96c65cb8fd8ddea32d0a7839..96551bba16a61295cb4c137f2b979382dd95f827 100644 GIT binary patch delta 104 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{Mvv5r;6q~50$jG@dU^g=(=Vl&(r_7VhMa34g qb8rYU162Tl05_0u1u5QG_?>w&zla=+`3!lUIr+&+Ir&K-bAYB70%_g zFyPcVIgO>6-Oy4;!PLlf@&^`sb(|_0^2&mX@^bR?(t+kNZY;daB*+Xln?ZsbNV|eO luvw7fJM(0I5zoo~JRBU15Wg`@j^|mZ-Hp!`=w@GF0sy72K|cTh diff --git a/auto-bid-admin/src/apis/scrap.ts b/auto-bid-admin/src/apis/scrap.ts new file mode 100644 index 0000000..c78ead5 --- /dev/null +++ b/auto-bid-admin/src/apis/scrap.ts @@ -0,0 +1,47 @@ +import { handleError, handleSuccess } from "."; +import axios from "../lib/axios"; +import { IScrapConfig, IWebBid } from "../system/type"; +import { removeFalsyValues } from "../utils"; + +export const createScrapConfig = async ( + bid: Omit< + IScrapConfig, + "id" | "created_at" | "updated_at" | "scrap_items" + > & { web_id: IWebBid["id"] } +) => { + const newData = removeFalsyValues(bid); + + try { + const { data } = await axios({ + url: "scrap-configs", + withCredentials: true, + method: "POST", + data: newData, + }); + + handleSuccess(data); + + return data; + } catch (error) { + handleError(error); + } +}; + +export const updateScrapConfig = async (scrapConfig: Partial) => { + const { search_url, keywords, id } = removeFalsyValues(scrapConfig); + + try { + const { data } = await axios({ + url: "scrap-configs/" + id, + withCredentials: true, + method: "PUT", + data: { search_url, keywords }, + }); + + handleSuccess(data); + + return data; + } catch (error) { + handleError(error); + } +}; diff --git a/auto-bid-admin/src/components/dashboard/working-page.tsx b/auto-bid-admin/src/components/dashboard/working-page.tsx index 399f74a..00b28c9 100644 --- a/auto-bid-admin/src/components/dashboard/working-page.tsx +++ b/auto-bid-admin/src/components/dashboard/working-page.tsx @@ -217,26 +217,32 @@ export default function WorkingPage({ data, socket }: IWorkingPageProps) { - - - {statusLabel()} - + + + + {statusLabel()} + - + {isIBid(data) ? extractDomainSmart(data.web_bid.origin_url) - : extractDomainSmart(data.origin_url) - )} - size="xs" - > - {isIBid(data) - ? extractDomainSmart(data.web_bid.origin_url) - : extractDomainSmart(data.origin_url)} - + : extractDomainSmart(data.origin_url)} + + + + {isIBid(data) && moment(data.close_time).isSame(moment(), "day") && ( +
+ )}
diff --git a/auto-bid-admin/src/components/web-bid/scrap-config.modal.tsx b/auto-bid-admin/src/components/web-bid/scrap-config.modal.tsx new file mode 100644 index 0000000..00f95f6 --- /dev/null +++ b/auto-bid-admin/src/components/web-bid/scrap-config.modal.tsx @@ -0,0 +1,163 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { + Button, + LoadingOverlay, + Modal, + ModalProps, + Textarea, + TextInput, +} from "@mantine/core"; +import { useForm, zodResolver } from "@mantine/form"; +import _ from "lodash"; +import { useEffect, useRef, useState } from "react"; +import { z } from "zod"; +import { createScrapConfig, updateScrapConfig } from "../../apis/scrap"; +import { useConfirmStore } from "../../lib/zustand/use-confirm"; +import { IScrapConfig, IWebBid } from "../../system/type"; +export interface IScrapConfigModelProps extends ModalProps { + data: IWebBid | null; + onUpdated?: () => void; +} + +const schema = z.object({ + search_url: z + .string() + .url({ message: "Url is invalid" }) + .min(1, { message: "Url is required" }), + keywords: z + .string({ message: "Keyword is required" }) + .min(1, { message: "Keyword is required" }) + .optional(), +}); + +export default function ScrapConfigModal({ + data, + onUpdated, + ...props +}: IScrapConfigModelProps) { + const form = useForm({ + validate: zodResolver(schema), + }); + + const [loading, setLoading] = useState(false); + + const prevData = useRef(data?.scrap_config); + + const { setConfirm } = useConfirmStore(); + + const handleSubmit = async (values: typeof form.values) => { + if (data?.scrap_config) { + setConfirm({ + title: "Update ?", + message: `This config will be update`, + handleOk: async () => { + setLoading(true); + const result = await updateScrapConfig({ + ...values, + id: data.scrap_config.id, + }); + setLoading(false); + + if (!result) return; + + props.onClose(); + + if (onUpdated) { + onUpdated(); + } + }, + okButton: { + color: "blue", + value: "Update", + }, + }); + } else { + setLoading(true); + const result = await createScrapConfig({ + ...values, + web_id: data?.id || 0, + }); + setLoading(false); + + if (!result) return; + + props.onClose(); + + if (onUpdated) { + onUpdated(); + } + } + }; + + useEffect(() => { + form.reset(); + if (!data) return; + + form.setValues(data.scrap_config); + + prevData.current = data.scrap_config; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data]); + + useEffect(() => { + if (!props.opened) { + form.reset(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.opened]); + + return ( + Scrap config} + centered + > +
+ + + - - `; - - inputsContainer.insertAdjacentHTML("afterbegin", otherEls); - - // 2. Tạo và chèn Current Price ngay sau #url-col - const currentPriceDiv = document.createElement("div"); - currentPriceDiv.className = "col"; - currentPriceDiv.innerHTML = ` - - - `; - - urlCol.parentNode.insertBefore(currentPriceDiv, urlCol.nextSibling); - - formElements.quantity.value = data?.quantity || 1; - formElements.plusPrice.value = data?.plus_price || 0; -}; - -const showInfo = async (model, formElements) => { - const key = await getKey(); - if (!key) { - showKey(); - return; - } - - try { - const response = await fetch(`${CONFIG.API_BASE_URL}/bids/${model}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: key, - }, - }); - - const result = await response.json(); - - if (!result || result?.status_code !== 200 || !result?.data) { - if (result.status_code !== 404) { - alert(result.message); - } - - PREV_DATA = null; - return null; - } - - formElements.maxPrice.value = result.data.max_price; - - createInfoColumn(result.data, formElements); - - PREV_DATA = result; - return result; - } catch (error) { - alert("Error: " + error.message); - console.error("API Error:", error); - } -}; - -(async () => { - await showPage(); - - const formElements = { - url: document.querySelector("#form-bid #url"), - maxPrice: document.querySelector("#form-bid #maxPrice"), - plusPrice: document.querySelector("#form-bid #plusPrice"), - quantity: document.querySelector("#form-bid #quantity"), - createBtn: document.querySelector("#form-bid #createBtn"), - form: document.querySelector("#form-bid form"), - }; - - const style = document.createElement("link"); - style.rel = "stylesheet"; - style.href = chrome.runtime.getURL("assets/css/index.css"); - document.head.appendChild(style); - - const script = document.createElement("script"); - script.type = "module"; - script.src = chrome.runtime.getURL("pages/popup/popup.js"); - script.defer = true; - - document.body.appendChild(script); - - handleSaveKey(); - - const currentUrl = window.location.href; - - const model = extractModelId(currentUrl); - - if (!model) return; - - // set url on form - formElements.url.value = currentUrl; - - await showInfo(model, formElements); - handleChangeTitleButton(!!PREV_DATA, formElements); - - formElements.form.addEventListener("submit", (e) => - PREV_DATA - ? handleUpdate(e, formElements, PREV_DATA.data.id) - : handleCreate(e, formElements) - ); -})(); diff --git a/bid-extension/manifest.json b/bid-extension/manifest.json deleted file mode 100644 index e59b97a..0000000 --- a/bid-extension/manifest.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "manifest_version": 3, - "name": "Bid Extension", - "version": "1.0", - "description": "Bid Extension", - "action": { - "default_popup": "pages/popup/popup.html", - "default_icon": { - "16": "assets/icons/16.png", - "32": "assets/icons/32.png", - "128": "assets/icons/128.png" - } - }, - "background": { - "service_worker": "background.js" - }, - "permissions": ["storage"], - "host_permissions": ["http://*/*", "https://*/*"], - "content_scripts": [ - { - "matches": [""], - "js": ["content.js"] - } - ], - "web_accessible_resources": [ - { - "resources": [ - "pages/popup/popup.html", - "pages/popup/popup.js", - "assets/css/index.css", - "config.js", - "assets/icons/*" - ], - "matches": [""] - } - ], - "icons": { - "16": "assets/icons/16.png", - "32": "assets/icons/32.png", - "128": "assets/icons/128.png" - } -} diff --git a/bid-extension/pages/popup/popup.html b/bid-extension/pages/popup/popup.html deleted file mode 100644 index 68ead10..0000000 --- a/bid-extension/pages/popup/popup.html +++ /dev/null @@ -1,170 +0,0 @@ -
- -
- -
- - - - -
-
-

Key

-
-
- - -
-
- -
-
-
diff --git a/bid-extension/pages/popup/popup.js b/bid-extension/pages/popup/popup.js deleted file mode 100644 index 34f60f0..0000000 --- a/bid-extension/pages/popup/popup.js +++ /dev/null @@ -1,17 +0,0 @@ -const handleToogle = () => { - const btn = document.getElementById("toggle-bid-extension"); - const panel = document.getElementById("bid-extension"); - - // Kiểm tra xem nút và panel có tồn tại hay không - if (btn && panel) { - btn.addEventListener("click", () => { - panel.style.display = panel.style.display === "none" ? "block" : "none"; - }); - } else { - console.error("Không tìm thấy nút hoặc panel!"); - } -}; - -// init(); - -handleToogle(); From cb3bfef75965f195503be715ea5940e5efa50b41 Mon Sep 17 00:00:00 2001 From: Admin Date: Tue, 20 May 2025 08:03:36 +0700 Subject: [PATCH 2/2] move extension --- bid-extension.zip | Bin 21484 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 bid-extension.zip diff --git a/bid-extension.zip b/bid-extension.zip deleted file mode 100644 index d55429df3e714589496d8afc6e1e525a5f0779fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21484 zcmdVCbySyI6E;jrBP}J}Ae{oz-5t{1Al3PmUk9y9t z*89EB`gGlF{KL8KYi9QBeb4MYvJ&7BXdu8pUrfu@zy9*a4@3|G5Is|aCx*`UhE{f_ z)>d>5&Tt^05a*yEAYcEBk|F{KRNO046W{|8LTV!WRTeY|^xtG*0%U1xOYsTFD5%oW zYJVs8cT#Jn$V~Kg(DjXV^mGoTB$W>zDhJAXgGFM3lOy4P9uX5fIK~pf3JakmgnBgh0R0(OHri8J&`#q39Ev8kCY8@#(t*)q{EPK+a!7*HlkVPfN^5iweAp zZ_s}xYokDY&;XkGO9y-^W#8%Z(ckn*E2yBYU~g?}_%js@ zpplpu7m}F($G!Mg=`MP-mk|Q~@Tv z0}0!jN>C5$M?0g={evIOj_Z}=aU5=cO?1-`TOTl!;9TJ@pt>bxZTR$2#aDUP(9oS_pCN!2($Cd|Gy7g^EWrr)73XOwzYP!GN3iH z`qF%FWF@*8;i)dQl@9 z8ZJ23YAUXu=Bj|7dkI=BqqsP1$?2KhaSW;*<8g>Yi6lHc{3$9Nc3Brlw%huZt6Kd?`O?)PYhPV-qs_?Zz8fZaM^HwOp(n7PHhmow)xap&=%J#RnL!iWnH&_^DRBBwA<}8%_U!14R zNr~7)U6Hb#(UYFy`wOa(ImH?t$D@$%Tru`1i4ARS?tbW%E)uXxt{V}igFY`n&<> zYd?Q2to+$;g^E>&I_yR^QgsnbQ@_9N1EV@~P2h9!<&+u0cU(y!LhZz|GbUEM)t>X`#dH_PU9Fc^ z3nj@nV8D1uy|ufi4hjO227Z50_|b&FjhFx0n!k6(BXNQUN7hV8khlW;z8R8{;g5Ms zLi7A#s78j{36fIMi{n*gBV&qGWMj4ClHwEMQ()!oFat?t*We^jLr_k{8BgS~FcUFp zEK0Ar)<)cazojybybkmO%)Pnq<{u1C#dljO;I{Jnbs8|~0J~M7h(Q46s~*%M?LdsuHw1DsN{3pXd^b3q&~X=!hjUt zYo9eF5@=iKND$E7Y~SmLcQJ}*eipXuZLBr_hapAVOw zGZMs``*X2Bb#6Uba=tsOKES&OVvA$kxo~}fJ?@+WJM*Twv2i9F>mYUUsEK!Rr?wJ$ ziQ`igw281lf9H$#iD2`#+Bys-vGBGzuUprHj(%`ePRbQbyoVtqv0PH3BV=0~Zld}O zZqlp1Fg)BIbELaOp&^0XdQ6~_bXpFv;~X(UX_5K))@l^saeXP~ucRZZ3!oDZCg^vg z9~oP`C*7cU$PRUZ&P3=g>V6j4;&o7lbSS6^hyVHMV*x4XK7O%sUhubdxoM+Q+S#y? zOA{{f8Y7&~&Fzehclp!MK=YV_Z!}6*`W52UWaG{)A4f zLWM?xbc?4ceQS1*yg9EokL!hXx{+0d={OO}bc+q;z!>T$XY z9jUeg0m+yse-^ngZ;WCS{t%nhlyw}s?76+CEg#$+CbASNSCWWA8c$oNKN`*Lp>dQwTc?5Hn1Q)^asJ(-OZyBmBTGR6(^*h( zXo3ATszSi#A93t-NG2_l6=&cI5A z_n2hc(@HW6Ji9xAK`JX@j|%_uF0REaX%}-krWbT+kt?!6&)S7^#5U(O_FR5-Pg zpNlZ4>ps?Z>zBy>#JAJU3)7xA`q$vne%WpR(w&3s)ED^%#|Fhx&eueA)Xm- z{s0zsJ)UXMJ(5}ViO>3OG-q-dGUHRfDI$d`m^{ZfMnfPmnw1Y^;mAGni#K)H%9#`k zWE$9W0@@a|prY{}!*W>TnPbb}2lYkW_XV)H`^Lcm<8MZe~0t%A2`?><{I< zquG&Z8$)RPf~3T4xbB4=rq6lZJUSMqSpoC!S20cdoIAWmrCS zb#-5H7@lCrMjjMe?B};;lOp--xM~o`0OIm+iF{plo3PQZHBTj&VGKR_~<2x#~*T!I=)NyQLdKXQT-;(PhiyTTTqaDeDNz zTca2ma_Lc9c&$*+W@i#>Ww5JxTTi&5L9;kOy47Ini?Zrg)~adLePc8`v(qb&1N1-1 z;DCEIB}}hIzH5CR&y`f)xmbNieR`$0DSx)Z$yMZ#cZpyWy{|T?{3xSy?fnS6?6g2M z!Y)s0PPei8(wW7Qi%fDS4a9kx&pL8IHuPJ=RuZX%dP54{b)vVFrya&=b0#?5MdA2i znzh7>@r(9_!=Aggt<G<7;g)M zli9SV!X%7$Ho>RnY*83V47*ds?1U0dAlqbfux3P83-Uf|Iv+CWT-kx3u`vsE@8Xf!LV! zwHQ&_Z!GZviI}_%Y`MfqFudOCN;jINT0NZz@Or6U>`*?jZ_qq1&t}fEf6Z>}HM;gC zst~rGZ4(X+XX#8m%l0S*&a;TjS8N%+uH0S{ys|BLBuo@tJ52O%jr&N~|gi{&97(7a^db+m41q3RF15n4I#Pqx)C3JS=y^Emh; zHvRnSK`@J7>>P#GLy&KBn1|OFk!TjSEx%MrvU&MOOS+;nZl?_euGQNTLjlBCut)WmW6LL_k@IZ=AB=r$OTpFXD*S^wijazUZ ze0nh)q%SxTCZ42h?bEXN&l5>(3l(j z$;_F9(7|?A3~%=uXAc~uu+`uaa_m!sNN&(bopD0m7yzqo*P@K2AF*!_AL* zeIQC(1xlXSMToK=A#g!ZMI4$e@p>BqKb=`V! zi=T{Z#bz#Vw+wn>=Ypgf)SOed``HX#df{Bf&dSf$IjJR&9~3%$3nh0nwQ+lc%YnEj zRIJ=JU>Lu};ttXW3NlRW9UP=|Cb1)O`|CK3vM?RRtG4pBrVgUc6ysxT4;G(ys+v%$ ztFp!*-9+y7&@L~!7e~6O28oxsw--!t-BUjymW<8u{2ShSjo#Sc;^3}VQlHx zag|u>1YAq3oG(G-an}dyYw@g164_tI^YM%)p*b*GaLv=Gx_}wk{@^G#J@{Y z{JTy1@4rU=zscV0TYjIZK>ynZhOV8Rq5aR(H{M(%CPjb;IR2Hs`B5bP?=A4#_tbr0gitu=0~x=nMZ$kBCR$6;b+?U55WIIJGB3pV*2ClO#!js=lW>? zq?TF%4{-cTKR*g4{q=16Q$JrZex{-SK>RN>6#qlD4f}8Y^KDMqSlc+*{4`s<_>$CQ z8}IE}NP|F87}OtqRQe;;#zsn+jPJ%892IKND-3=ExV ze=!eg48G+mfn%*wzttQ)QtK6UAhr;OYEYcT>o*wzX|s5)xIwXk{uq$a!KoNG7q+h- zhaip<)xv+kwZ&T;YQfUf&N38w!S5J+8hwnXgv*ny4d>5FpgtQ&$5y=EBxE|qXYSCecklYc!dFZnkz;>J*)m+4aH?h}TzsRUSPT~EwuaNt>NXwHiGQCLr3zyexg4v$!n ztu^d2+c|1;AfHe|>rOj2b;{K{A(s^Dy#Tq=@X}XLp}{cRiaz*(hfjqjkPEBhZ-Aj% z4!n9Cogx<+CnUG)gaVonUM1*MEos0uvbK|uyCt$w&jdw79+4?Uo=uIT6>)43YBeXk z-N6sfc1TI89OSMjIhZ5JeITYN=4-u*?W#nn?%n2jS@W#!76)B;$)d&f;f_Q+{W!(K z3)9$LHC(3Xl@$zreEtg>svt1 z^GuS};V!a*JYv`-MfMG$6BIY;r)fUXjWi^WtwyJZPg`sk2=t|wJ*6yrCvu$9lH%+p zQebZ#PJ0&~TwWYA(hqZqGJz2K1wQcfl!Tzw^C9wEAw6SyGL}b6EAUB4O`+%OSvh(w zo?IC4^o|8+JT6b$-HWe!7^g1?-YMIpl||lZeR?;5pTEt0>M*o9vSBM+*pQ%S-Y`}U zex+jm@|s9jc%43gMIffQ9Y|waQ%}BLziJrQD$eTsN1iV zG!P{Wm_9HN5NeqF8?hhf&68ix|9caNR{P&gq2HF{|LreVH8iwyIEh zc~za!!Djt0`o18=ghF4@==l>SF;Qj_76}C{u3lZOy6o>bXddi*<|@DPSQK;Slz?G? zWI%!@(RH}YA+D%$#FL{qbi{p#3W{-PC^CqeQKU5q23_OUxt-H!7kVL&_$4_iM!wgj zSLK}ir1_}hQ&Jsh{>AeGQ)~>!H+1#|P1dU#u9vzOt$uX}@}x}V1W+d?+q)B34}|hz zVXb>2o_o!r>LqD%-wALE@EL>orpZWwgQA<9y;%@xFkkuj!6I#=3xyn0q{k%j_wr zik*R);dk!j;!8U`nh7~=J~%XzbzzP}^mr1EymC~r^sc_d%|A!c%2^~OHG*U9|X34fo!{ZzoU~wV%5=o~`6D!>lphh481& zHLuu$CEd;w2a%sRP@bLeLhMl79IZRF>TX$jO%o(B@JFyL*&&IYTPnL&@{+UBtYry> z*OoHcphLDD^qDT{nNh~3$9hZH@fVMT_|Z9zzf2BAd}%;k^|<>@p6}MDjyf)^7iOOf zf+G!4O`khhs(Mt{u9;1shj40c%cW-Q!(QC@*44R}$Wy;g_0r{k-!CARuYjI)(?DiJ zUM6t{??h+t3Qku_m|#Mw=n41=v-IZSg4d$#=gkdIq_eDw*K@eHL<2!3CB4zhYmWIN zQobtlL;3Px5bvN2k-gr!j5(BtV_1$ik{0CXaQ7`sM-!xfm?28#W`oG^z3wLT@5o1P_i-ShXiO>h@gdW88%gG*1cB)7`yxMQbgZ zqAjHy^8!!YY?9;B^EYf=vf2CyZ=-T#TOT8wd>)k&8@i%+rh@#4T4f4R@T!O5NpnFM zcIi;y8E3kvGGi>;Dgs`Wvt@lsXozQ^^8WhbdXTYj@O1B_P|5&)r0g=CL7~;Kf?5$T z+a`CaT|s$3UgB1{mios^M={)VwPp;4+a=C`CYxdkXNP(C z>9ipy5pC$9zD+Y^Z1cH<8cAP9uu!+43uink@&Tb=xfXt zjGNpS`#Z2%dL$w(p7 z7rc#f=<+9hI1brW#}q}icso0vVGH_R$isU+f?__tiiYnpww*fL`8eRU2=%zcEeVz) z;^h{(|5hr@mu51I%@~jyIxpU)1)-M0@df+ney_UN_w0P;ZzlR7f>(s2^!(ZNwk^f6M{b?VIL_PR%CXQlPx); zs$Y21F@F4>CM~AM^%XtKg@JvVuXS|md&k+8bb;t9MRR+N<_Has>*v$>nGfPe2)W93 zvTDe9Be2|&C%NW3$l}cg6AM2QOCr?zw#nVtJ$;&r+2XnGDS1WG!-0hx5mQ9!Nku-o zWSJQqcr{}v|5QQLtbh;qD5Lxmyq*F<(S;{4l}^dLLV1lL#0$}z#&s@-iU`%lA=n5L zd~rpgTpOG46%jAfXI9I}V%f)PGUU)x?3YNc&X%p0WONmx^WlIP}2yo0?=fHm&! z2{>x}SVHpB%hV_&=+TGH$xY3{jrp`Lht_N3LxgmT;K?&eiCLT;n)OEWaO~U>F-vLS+8((OJAYL>_l}l7c#euOjTF8#%X)BZKZiDspBSeKCljipwP zIN%`^8u$jPJm{8I04dTr2O{)AV(?J-D1Z1g_&bWVq3mSyQs0qb$*MStvuxqRW`W-2 z$^%!yhL;SaJko~?bX}h3(A6gsgOR0!aW5h$>zR^`yXfhb=Z8m91WTkkc{&4gqol~< z@hUDqUl!rwW2YV*nfvrNa$L8U#33t^!DN%o%G5QqNv4AwEhiH`1AP@tgKVoKoQ9~k zHrAfpL~v2~;%TFh<(1v2rrr_Vr&gKbSG>#c5}`hFz1dw&%Fl;U87GoZMz8p^yrVJB zd5s09jRq$JPp6BW^;SEzValL6@am(aWgoTNl@<`uAmt)r-fZ;K?LZZR*~=9Bwi?A| z5L%XaFnu=PUi>)Dv>eUo-OKNqma^1pow6-mgzInqPJaSVPR-MX zJvL7**;pyz6|aUD^gIDRaQ&c)bv4r(+UHkiM@2MHiOeh$W$` zCEKBk`X!vt6vKLR2Bq*4Z0`cq)Gk5q0w;ZXLubAqlw5gwD!e5so>-fa+3)CDnESo$ zqqyEgO>zl1134#roiOxc#6$B*+|Pppt~dc9Q+bV4Exl?>&b!KsskL$trx8Al1GXx#K7@%7^97_@g_^vpKf zAj1Q7-%eR?uW6nc2{{>uF+4V4!`(y?h%ON7LJPHuCXOwey>Ow{3!jRa9N4= zIMA9x{N_0Rju-B`Z@p6fVNQ|WOEaM5QZLh+j%hQ-HvYu@kF*8m) zm9(Hs<}zg?Ono4JUR%O_$2RC)(=bO-T&R5}D<#=0b`%7Zuy@y^U}Gu!i_S-KE1xM;QxIc|&Hmz2rm9a$0Il4Kqh$I_VTA zJxel4IVlfFoWH%zsHGx^&i<%LHmZoDP9ln}Z}0{uy#sZ)N;hI?jL*ZwV900ki9U^I zuuPEj7?6qecf+t?p?kPc^8p#DitGg^1|rC~-j8{(n<@-JG8a0tx~|3`Yq@j1h-gM; z64<>q%>x)&H-~EWvF9RQpJKnP876@Hs0q$fe8I!tUGuKQMA>6rrnLr+jz9`40iX6| z-@fr%kIRz9lIn|&k2ToFWV^;Z$)46cuh?qa?x*%4`9i*?AbE0WuutqHxz2^eNS}ywkau z4YonMKE%TkDSlRl!U8r~6NRszEQui4B5I5Wgc`rVB$8U}bv{)S$P+HDlj&%>$S^nr63{>SUW{&$wK3<_0NyApATy)~mPRQCgGiCXY4m`X!9(Kk)UNU9 z17iq(9KmB60)8XhM_ce=SvoUE+?OwwVQ}>ABs4R{cv5T<87pE?xSJxyKje{=zi=#5 zU=deGI@m>RamK0yBzaAq?ZWDt-e4`1_E~R&xr~DrD^x+wmW2Hvi8BS|MH)s_^UkXAL$d;Uu92%0ojvAK=vdKFoH6Gj@$;xN{b-C zV#5MX=pl%S3d#dUfGRq(k_+LU*W(e!`;R5)mbiB1eSZ8QOoku%CP} z+~Q)L(e|LB3(<(Zqo|Ra^|kV~SGOYuya7Hj{E>e-imbS_G{HmXE}{7n2#w{E+#3Iq z)m(@c{cOlUSt?^CFxI2s+GnDB$n`6(+>vwGNC6joaV$%C>7O?n`g0%zlMy`w)*B#Q zdaO`I`?qdZt_oyFWNdMx!H3si5UZsHooE?)U9fi`>`7HPvD;aCX2RF|OIMNFiFPjD zL<=A;J#b4{snNf@^?w7}o5?`KVKO2Fpba7>BrRCRuj31>o*wtw7_x$bfG_}_zmoPp z8i@vAq(86A{50$Rr`P?Xz5ajTbWDuDb2?zkE)nQ-uwZ95KXEz&fQkveI-Q-X>nY$L z_ni*38|ZYZB5&il=^**gct-`GAz@k}cmjkeA|oRMJMlBdC=`#e!ze@r1qsnM{pDBc zMc206U$!!;^t8<^Brd2OI3zncjm&UfjEMLhztPfpqzcyNEuaJL{1B@O8O7r5Ip{{a z;`R-k4y|BTC@+SFrUpvq7TB{1d|WD72q!VoIB8H9m9_WYj}YLI4`1e!t`$c}miBoW zx4RecQgF$vAYhJd9rp7+wArFyU%Z@38k3X+7s#_aL{^1FbT`F%QP)jkgU$#!zBq^u zx#nOTa%rGLZJN0U4JHkP@1whfoKPUpDZ<;3-Of#q_qEcfTrq>#rqn0ALrI66GJH6trLl;cV%VjF5mKgIR9Lpu zW|$Wa(;j*fQN_o@PP6LqEo2$kjo5=Eqx_iS+Ii45z-f)BP}ZaOyo>d99#oW{`(FW! z``v8XOuK@82uOj$0-paHv+185@qaO!5Y7In6n4K<__dn%i}TN|fQu!-J7vJpLyk|p zn;vapl(J0?{t0_(s{p#cT3^E{nB}o$R}PN=P5|ZjYmq~+CYuduzD*jp=#Sm$b$#A? z7=$frtrv?SHHsX4sPYdEN`~S2;rXDCNV!Hp)@)Lr>M5#yktJxP5U6L}jF>(PgY~b* zI>9JZhT~$-5r2-dydBUc0X^&m_9j76lU6tTYE=QWOmfK^Qg-_32Pe9iwB2XzXT8QN ztsYY;nXfEAddT-!cs%q;cYTJh9Uy>xD3&Eyc$U^`?eI2E^#MQjllqlwl6q}$h9V71 zdikbVEY`p~;Z;lsHGWVP0(pHk1X?l^mOd5%e1wcb8{5NAs>&TzHC)|B36C`{MxsvI zJc@g}8~5huI4;w|YFVpaajv{#HhuKDO`2y6d=VJ1)y$lp5Ceks^mhUKN0a+< zLuM!jh0{mMTHzC=0Uj(M;a`~8Z*zKFKB}TLj3xi>sksFrpD@_WZdsWV;4uB$CNJk0 z!<$Q6hlTPfT{k->`=SLaM5#Ah=hP)(0UMdk8hds6UYqsFC%Y1NlckG>({o+AZcb^5 zwilpR?MpyU?%q z*gTg#wdBDlnOO1PJ>HY4t2E#E*fwYcBGM9X-CKv%lM@N;; zHE$ig?2HDgxWI!Tm%dDlS+})4DAT=T(&Dyi(CAQ~m_AwAsno`|qEj(qbr`g&OmW+6 z?!9=bVz+~79m7KK zrHe)U3r)d=xB(0$tXSF-hoN}6-cO7uK^U1R`r*cq125T5p;;=}^^Z<@vwf!59%#=V zp_a@MCDmw*$Iy4fCU>Bvy+?xX?vs*p8H}wk;+hysAXS!H&VthT?XLrn7%hnHAL=!}qR%Zr zxt^lWdR4VOTWum8naL}ILk9WYuLoAALrBTBBubuJ@VFwF33NA>mdX=M zfga-F1k$t(*28&Th4;))p86nr_jJuvEEe-JlKJt;1>5sI2r(oT9tnEp$KY=)7;cXW zBe%EB9B!c2AncEq3SC-*%H}sFUHM=|L?8U4i&AVZ(u4T-0|Mre%y!Py_?AS^8T-Z2 zhDrPq3Z=kMB60+HW?rk26Y`Tzn!Nu+G)R(JtG*;k`GQC|m{_AHx;L6DN2aTZ#Zvw$ zx4C>%fa45BI06Zo!48&VCy3;16c1l|sPCSos;7W5X=-&1T2a&Z1k_eX?IAgPJ>_!&UFFKKl2t^3H z_%UAfndd|cClhSMNPklsS|^}tfXwN4`z}^W!I~5g~x3VW743TM@DNdnDljWLshh$lZMEP3evnSa0 zJ60zUORl)sqliW49Ztr)Sy~$;hYY&#gZjj72qA_=8k1un;u^La?;lnT3GR^r?I%G@ zAdvz`D%`Hm9kGP;^a~7mE0>|GxGPyY)Ir7zi)L8!>-SF#o9FE;UW`=3m!USe*HC`? zlya=P>W_XMQ>!Ow3;`Aac|@Zt`6VpDm}6UcJi}Y^SgunS=^3Q($ctb*Tqoo$IMpRhe`2QjUsCZ>8F%3>r5+qhu0--{4pg ztF9-^!9sR3c>56xZG-(SN4S6t+-j+F9eZOAco(}6Ixa$tzI={s=q8#i8p~^cT+jzF z5b2RYO_ZZSZ*2Wt`ynWU9bA=D;`@lu_IsI?8F9MdH#qe^y1bAZ!Kp>l*_DWnZwM7o zBE|~qEydL^Of%yTG%?UTx+JJgiM6NMZqL=~%Uv;75|T?-LAlbvTt761v&*HU#NL{u zY=qB(MZZ-;N6%HIz*EDXGQJiSq8?x?(@jm;)IdhDhDO^9k!?z$R+Qr8x13OU{zyqm z9;A3GeTzdLSrWFlWRR$RSp&w5aE&f_ z-?~^|&eO4EZq&+%^!jdU8gik}Ks42d$rM4OwUPb##X}YAj~X;?C?U7JoXo^lt`S#8 z_0Ma_F&CQfodiy}-mJc{cz(Dx*;Ej5>l!ZEx1UZeG3`jN4-jDkOp8Z~OkBnR8;|`t+ToN@kEbXl`@Fi%F*MxM5A)Y4=k~7Zt}KuF z9qhcE5MI(RG_P-|%$|q@-94R;;F0zS(7Z!G_G-0e4Y)nLChopVJZ}-cs(lEv>2K2V zrfnx?PhItFtd++?Sq0zO8P>CdA2D$)aqzg-O}YH@MuRzPX#D$f9|uI4MJ6j553+h;8)TBnW{f$R`PTigLx3kXFDtFIb8-&I7#A;3!EO0vi5BXc_l za?w5*kTdmZhLWMxPA~R8L7JlVTicR&kMZHf+EYb_27{$BCJI98Sg-3k0!Zv9+HSOG_4%A$g<(z!`A zBP&Ds!8UXg7gT%?sFEB)s$U#2#iK%YW*=I_T!a=psJEN78D1Gpa%B#^i@vvaWc^f8 zr%cg~!c|()*Ue?RW4SyY^mx;dvSa82CiBc>(^H72g+>?VN#;+b~47b^i);S@>E$)ghYmz$djzjq#YOCNOah|Mym zViWHU3mI4+K88N2Ji=O?ei%mFbb9#+xcffBk2~W8WSC~L?|H6GoQp5r46_+a|}jep%i10WpO=0sNFuU7{Ozuypp z=sV%ZfDisZU;PJ^-*oEu2@bHm#{HfYIN#yK18!Y^FaGz1 z`2FR401;q|h5JN+0QYalfA3EvfWx4_w8Vdn1-#XB|Gv-t_>tq24G7HNlljfX9sm>I zt^4~-fYR@qg!rCM<@j)8{D#TD2LeuuAFtq`-vip?gLw2KjXw^?{w%_NyW8_um;#Ev z&jJ6wXde}T=wDjn8_GX{0N?+|+cMuzYivMF|K1GW7QLTI{&oxJuh$1kzW@5)PiqT+ z&LghcP#m5;=kRO`HKQj{Cx`dy=c$pB|!WyDExu* z^ZPRZcfAh;p_J70}nV;2mk;8