[{"data":1,"prerenderedAt":1390},["ShallowReactive",2],{"projects-en":3},[4,178,333,520,685,1224],{"id":5,"title":6,"body":7,"client":151,"cover":29,"description":142,"extension":152,"featured":153,"links":154,"meta":155,"navigation":156,"order":157,"path":158,"role":159,"seo":160,"slug":161,"stack":162,"stem":169,"summary":170,"tags":171,"thumb":175,"year":176,"__hash__":177},"projects_en\u002Fprojects\u002Fen\u002Fawp.md","AWP auction-winning bot",{"type":8,"value":9,"toc":141},"minimark",[10,15,19,22,31,35,38,61,65,68,71,98,101,105,108,111,117,121],[11,12,14],"h2",{"id":13},"overview","Overview",[16,17,18],"p",{},"The company buys parts in packages won at auction. An auction ends at 16:00:00, and whoever has the highest bid by that second wins. Timing decides it, because the last seconds matter most.",[16,20,21],{},"I wrote AWP, a program that bids for us. The name comes from the sniper rifle in CS:GO, because the whole game is one well-aimed shot at the end. It runs on Python with the CustomTkinter library and works the same on Windows and macOS.",[16,23,24],{},[25,26],"img",{"alt":27,"height":28,"src":29,"width":30},"The main AWP window: a table of available auctions at the top, bidding and thread settings below.",1440,"\u002Fimages\u002Fprojects\u002Fawp\u002Fglowna.webp",2560,[11,32,34],{"id":33},"three-versions","Three versions",[16,36,37],{},"The program reached its current form in three steps, and each one removed the bottleneck of the last.",[39,40,41,49,55],"ol",{},[42,43,44,48],"li",{},[45,46,47],"strong",{},"Selenium."," The first version opened every auction and clicked through all the offers, and to read the minimum price it had to open the details. It ran slowly, because every click waited for the page to load and render.",[42,50,51,54],{},[45,52,53],{},"Six browsers."," The second version was multi-threaded. It opened six headless browsers and split the work between them: some refreshed the offers, others placed bids.",[42,56,57,60],{},[45,58,59],{},"Direct endpoints."," The current version meant working out what gets sent to the auction server, and how. I found the endpoints for placing a bid and for refreshing offers, so the browser was no longer needed. Optimisations followed. Today the only brake on the program is the money cap it's allowed to bid up to.",[11,62,64],{"id":63},"bidding-strategy","Bidding strategy",[16,66,67],{},"The program never bids first. As long as nobody raises the price, AWP waits. Only once someone else places a bid does it outbid them right away, so we don't drive the price up ourselves.",[16,69,70],{},"You set the rest of the behaviour at the bottom of the window:",[72,73,74,80,86,92],"ul",{},[42,75,76,79],{},[45,77,78],{},"minimum and maximum price step",", with the program drawing a random value between them, so it doesn't always raise by the same amount,",[42,81,82,85],{},[45,83,84],{},"maximum price"," it's allowed to bid up to,",[42,87,88,91],{},[45,89,90],{},"outbid only after someone else's bid",", described above,",[42,93,94,97],{},[45,95,96],{},"start moment",", when the program begins placing shots.",[16,99,100],{},"The program splits into two tables. Filtering and a list of all available auctions sit at the top. Below them are the bidding settings for the offers we take part in.",[11,102,104],{"id":103},"threads-and-refreshing","Threads and refreshing",[16,106,107],{},"At launch you set how many threads to give the program. Each active offer gets one dedicated thread just for placing bids, so nothing blocks it. The remaining threads refresh the offers.",[16,109,110],{},"With eight threads and four offers, four threads shoot, one per offer, and four refresh. I spread the thread starts evenly over time, so at any moment one of them is just finishing a refresh. As a result offers refresh about every 50 ms, and firing a bid takes about 10 ms.",[112,113,114],"blockquote",{},[16,115,116],{},"The third version bids straight through the endpoints, with no browser. The only brake left is the money cap it's allowed to bid up to.",[11,118,120],{"id":119},"results","Results",[72,122,123,126,129,132,135,138],{},[42,124,125],{},"The third version bids directly through endpoints, with no browser, so it reacts far faster than the Selenium versions.",[42,127,128],{},"The sniper strategy means the program never bids first, outbids with a random price step and holds a hard cap, so it doesn't overpay.",[42,130,131],{},"The program wins 99.9% of the auctions it enters.",[42,133,134],{},"Offers refresh about every 50 ms and a bid fires in about 10 ms, straight against the endpoints.",[42,136,137],{},"One app for Windows and macOS.",[42,139,140],{},"Won packages go straight into Stacja, which processes them into parts.",{"title":142,"searchDepth":143,"depth":143,"links":144},"",3,[145,147,148,149,150],{"id":13,"depth":146,"text":14},2,{"id":33,"depth":146,"text":34},{"id":63,"depth":146,"text":64},{"id":103,"depth":146,"text":104},{"id":119,"depth":146,"text":120},"Commercial, automotive","md",false,[],{},true,5,"\u002Fprojects\u002Fen\u002Fawp","Full-Stack & Automation Engineer",{"title":6,"description":142},"awp",[163,164,165,166,167,168],"Python","CustomTkinter","Selenium","Web scraping","API","Multithreading","projects\u002Fen\u002Fawp","A program that bids on parts-package auctions automatically. It evolved from Selenium, through six headless browsers, to direct endpoints with no browser at all. It plays like a sniper, because it never bids first, outbids with a randomised step and holds a hard price cap. It feeds won packages into Stacja.",[172,173,174],"Automation","Reverse engineering","Desktop",null,"2025","hl0xnxrm1AtQmR1V_AUqKIP2UgBp3PoMhNgXACACg10",{"id":179,"title":180,"body":181,"client":309,"cover":310,"description":142,"extension":152,"featured":156,"links":311,"meta":315,"navigation":156,"order":146,"path":316,"role":317,"seo":318,"slug":319,"stack":320,"stem":326,"summary":327,"tags":328,"thumb":175,"year":176,"__hash__":332},"projects_en\u002Fprojects\u002Fen\u002Fbrogear-ai.md","BroGear AI platform for B2B companies",{"type":8,"value":182,"toc":301},[183,187,190,193,197,200,207,211,214,221,224,228,231,234,259,263,266,272,275,278,283,287],[11,184,186],{"id":185},"the-problem","The problem",[16,188,189],{},"BroGear is a B2B platform for repair shops, body shops and mechanics. Instead of hunting for parts across Allegro, eBay, Ovoko and one wholesaler after another, the customer orders everything in one place. They drop in a repair estimate from a damage-appraisal program, a file of part numbers or a photo of a handwritten note, and we find the parts.",[16,191,192],{},"I built three pillars. The search engine over more than 60 million listings runs on Elasticsearch, BroBot is an AI agent powered by DeepSeek, and the third module reads estimates via OCR and matches the numbers against the catalogue.",[11,194,196],{"id":195},"ten-sources-one-order","Ten sources, one order",[16,198,199],{},"The homepage shows the heart of the service. The customer no longer searches Allegro, eBay or Ovoko. They place one order, get one delivery and one invoice for everything.",[16,201,202],{},[25,203],{"alt":204,"height":205,"src":206,"width":30},"Homepage: order parts from many wholesalers in one place, with one delivery and one invoice.",921,"\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fglowna-2.webp",[11,208,210],{"id":209},"_60-million-listings-in-about-100-ms","60 million listings in about 100 ms",[16,212,213],{},"The search engine is the heart of the platform. The customer enters a part number or a fragment of it, and the system searches over 60 million products from various wholesalers. A dataset like that is slow to search with an ordinary database, especially with an incomplete number. I went with Elasticsearch, so a result comes back in about 100 ms.",[16,215,216],{},[25,217],{"alt":218,"height":219,"src":220,"width":30},"Search results: listings from many wholesalers with a quality legend and prices.",1452,"\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fsearch.webp",[16,222,223],{},"The results show listings from different wholesalers and different quality grades, with a legend and prices underneath. Instead of everything, you see the three cheapest offers of each available type.",[11,225,227],{"id":226},"the-agent-asks-instead-of-guessing","The agent asks instead of guessing",[16,229,230],{},"BroBot is our chatbot. Underneath it runs an AI agent on DeepSeek: the LLM is the brain, and the agent runs commands to gather data and fill in the gaps. It replies in any language, and on the site it also helps the customer leave an email or phone number.",[16,232,233],{},"Here's an example: I ask about a bumper. BroBot asks about the model generation, whether the purchase is for a business, and requests the VIN so the part is sure to fit. At the end it gives a link to the shop.",[16,235,236,243,244,243,249,243,254],{},[25,237],{"alt":238,"height":30,"src":239,"width":240,"className":241},"A chat with BroBot: asking about a bumper.","\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fbrobot-1.webp",1719,[242],"chat-shot"," ",[25,245],{"alt":246,"height":30,"src":247,"width":240,"className":248},"A chat with BroBot: asking about the model generation.","\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fbrobot-2.webp",[242],[25,250],{"alt":251,"height":30,"src":252,"width":240,"className":253},"A chat with BroBot: business purchase and a VIN request.","\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fbrobot-3.webp",[242],[25,255],{"alt":256,"height":30,"src":257,"width":240,"className":258},"A chat with BroBot: a link to the matching part in the shop.","\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fbrobot-4.webp",[242],[11,260,262],{"id":261},"from-estimate-to-cart","From estimate to cart",[16,264,265],{},"This is one of the most elaborate parts. A shop receives a repair estimate from the insurer, with part numbers on it. Sometimes it's a Eurotax file, but also a PDF, CSV, Excel or a photo of a note with handwritten numbers.",[16,267,268],{},[25,269],{"alt":270,"height":219,"src":271,"width":30},"BroGear PRO AI: the estimate goes through OCR and DeepSeek, and the numbers land in the cart.","\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fbrogear-ai.webp",[16,273,274],{},"The AI agent runs OCR and reads every file. On the OCR data, DeepSeek finds the part numbers and quantities, then returns them as JSON. The agent searches Elasticsearch and, on an exact number match, adds the part to the list.",[16,276,277],{},"OCR makes mistakes. It can read 0 as O or I as L. So the agent also looks for variants with those substitutions. If it finds a matching part, it adds it but marks it in a different colour, so the customer can check the match themselves. Everything lands in the cart and on to checkout.",[112,279,280],{},[16,281,282],{},"An insurer's estimate comes in as a PDF, an Excel file or a photo of a sheet. What comes out is a ready cart with matched part numbers.",[11,284,286],{"id":285},"what-reached-production","What reached production",[72,288,289,292,295,298],{},[42,290,291],{},"One place to buy parts, with one delivery and one invoice instead of ten tabs.",[42,293,294],{},"Search over 60M+ listings in about 100 ms thanks to Elasticsearch.",[42,296,297],{},"BroBot asks about the generation and VIN, reducing the risk of a wrong match.",[42,299,300],{},"The estimate turns into a cart. OCR and DeepSeek pull the numbers, and uncertain matches go to manual review.",{"title":142,"searchDepth":143,"depth":143,"links":302},[303,304,305,306,307,308],{"id":185,"depth":146,"text":186},{"id":195,"depth":146,"text":196},{"id":209,"depth":146,"text":210},{"id":226,"depth":146,"text":227},{"id":261,"depth":146,"text":262},{"id":285,"depth":146,"text":286},"Commercial · automotive","\u002Fimages\u002Fprojects\u002Fbrogear-ai\u002Fcover.webp",[312],{"label":313,"url":314},"Visit BroGear.eu","https:\u002F\u002Fbrogear.eu",{},"\u002Fprojects\u002Fen\u002Fbrogear-ai","AI & Full-Stack Engineer",{"title":180,"description":142},"brogear-ai",[321,163,322,323,324,325],"Nuxt","FastAPI","Elasticsearch","OCR","LLM Integration","projects\u002Fen\u002Fbrogear-ai","Search across 60 million listings and a pipeline that turns a repair estimate into a verified parts list and ready-to-buy cart.",[329,330,331],"AI","Search","RAG","OEVqoq2TM655WEiIIf8R8jH_b1Mq0yXImytwBtv2n60",{"id":334,"title":335,"body":336,"client":309,"cover":498,"description":142,"extension":152,"featured":156,"links":499,"meta":503,"navigation":156,"order":143,"path":504,"role":505,"seo":506,"slug":507,"stack":508,"stem":512,"summary":513,"tags":514,"thumb":175,"year":518,"__hash__":519},"projects_en\u002Fprojects\u002Fen\u002Fecommerce-oe.md","DwParts online car-parts store",{"type":8,"value":337,"toc":486},[338,340,343,346,349,353,356,362,368,373,376,382,386,389,395,399,402,414,422,426,429,435,438,442,445,451,455,458,464,466],[11,339,14],{"id":13},[16,341,342],{},"DwParts is the company's online store for car parts. After the first version of the store, I analysed the site traffic and saw that customers most often search for parts by OE number. That's why I put the search right in the hero section. The store runs on Nuxt, and payments go through Stripe.",[16,344,345],{},"Our own store means no marketplace commission, so the company sells on its own terms.",[16,347,348],{},"The hardest part, though, wasn't the store itself. It was SEO. The company keeps several parts under the same OE number, so each variant had to get its own indexable URL. I describe that below.",[11,350,352],{"id":351},"homepage","Homepage",[16,354,355],{},"The hero leads straight to the search, because that's the most common customer path. Below it sit products on sale, extra promotions and bestsellers.",[16,357,358],{},[25,359],{"alt":360,"height":219,"src":361,"width":30},"Homepage: products on sale.","\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fglowna-2.webp",[16,363,364],{},[25,365],{"alt":366,"height":219,"src":367,"width":30},"Homepage: bestsellers.","\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fglowna-3.webp",[369,370,372],"h3",{"id":371},"search-by-oe-and-by-car","Search by OE and by car",[16,374,375],{},"The customer enters an OE number or picks a specific car, and the store shows the matching parts. This shortens the path to the right product, especially when someone only knows the number from the registration.",[16,377,378],{},[25,379],{"alt":380,"height":219,"src":381,"width":30},"Searching for parts by OE number.","\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fwyszukanie-oe.webp",[369,383,385],{"id":384},"store-and-filters","Store and filters",[16,387,388],{},"The store page offers filters and convenient browsing of the whole catalogue.",[16,390,391],{},[25,392],{"alt":393,"height":219,"src":394,"width":30},"The store page with filters.","\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fsklep.webp",[11,396,398],{"id":397},"oe-variants-and-seo","OE variants and SEO",[16,400,401],{},"The hardest element was SEO. The company keeps several parts with the same OE number in stock. For example, several identical bumpers, but each in a different condition, a different colour and from a different package, so the purchase cost of each item can differ.",[16,403,404,405,409,410,413],{},"If the product URL held only the OE number, all those variants would point to one link and compete for the same spot in Google. So the address is made of the OE number plus the specific variant, in the form ",[406,407,408],"code",{},"oe-number?sku=X",". The OE number captures traffic from number searches, while the ",[406,411,412],{},"sku"," distinguishes the physical items, so each variant gets its own indexable URL and doesn't cannibalise the others.",[112,415,416],{},[16,417,418,419,421],{},"Every variant of the same part gets its own URL, ",[406,420,408],{},", so identical bumpers don't fight for one spot in Google.",[11,423,425],{"id":424},"product-page","Product page",[16,427,428],{},"On the product card the customer sees previous prices and the lowest price from the last 30 days, in line with the Omnibus Directive.",[16,430,431],{},[25,432],{"alt":433,"height":219,"src":434,"width":30},"The product page with price history and an Omnibus-compliant price.","\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fprodukt.webp",[16,436,437],{},"Each part also has its own shipping, calculated by destination country, so the customer sees the real delivery cost for their location right away.",[11,439,441],{"id":440},"cart","Cart",[16,443,444],{},"In the cart the customer enters discount coupons. A guard makes sure you can't add a part beyond the stock on hand.",[16,446,447],{},[25,448],{"alt":449,"height":219,"src":450,"width":30},"The cart with a coupon field and availability control.","\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fkoszyk.webp",[11,452,454],{"id":453},"payments","Payments",[16,456,457],{},"Payments go through Stripe, meaning card, BLIK, Apple Pay, Google Pay and other methods, plus plain bank transfers. The store works in three languages (Polish, English, German) and four currencies (PLN, EUR, USD, GBP). A customer from the EU who provides a VAT number gets the price without the 23% tax, but only when the number is active in VIES. The store checks this automatically, so a number from outside the EU (the US or Asia, for example) won't work.",[16,459,460],{},[25,461],{"alt":462,"height":219,"src":463,"width":30},"Stripe payments with support for many methods and currencies.","\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fplatnosc.webp",[11,465,120],{"id":119},[72,467,468,474,477,480,483],{},[42,469,470,471,473],{},"A separate SEO URL for every part variant (OE number + ",[406,472,412],{},"), so variants don't cannibalise each other in Google.",[42,475,476],{},"OE-number search in the hero, because that's how customers search most often.",[42,478,479],{},"SEO and GEO, meaning visibility in Google and in AI model answers.",[42,481,482],{},"Stripe payments (card, BLIK, Apple Pay, Google Pay) and transfers, in four currencies, with shipping calculated per destination country.",[42,484,485],{},"Three languages, the Omnibus Directive, and a 23% VAT deduction for EU customers whose number is active in VIES.",{"title":142,"searchDepth":143,"depth":143,"links":487},[488,489,493,494,495,496,497],{"id":13,"depth":146,"text":14},{"id":351,"depth":146,"text":352,"children":490},[491,492],{"id":371,"depth":143,"text":372},{"id":384,"depth":143,"text":385},{"id":397,"depth":146,"text":398},{"id":424,"depth":146,"text":425},{"id":440,"depth":146,"text":441},{"id":453,"depth":146,"text":454},{"id":119,"depth":146,"text":120},"\u002Fimages\u002Fprojects\u002Fecommerce-oe\u002Fcover.webp",[500],{"label":501,"url":502},"Visit DwParts.eu","https:\u002F\u002Fdwparts.eu",{},"\u002Fprojects\u002Fen\u002Fecommerce-oe","Full-Stack Developer",{"title":335,"description":142},"ecommerce-oe",[321,509,510,511],"Stripe","TypeScript","i18n","projects\u002Fen\u002Fecommerce-oe","A car-parts store in Nuxt. OE-number search in the hero, a separate SEO URL for every part variant (OE + SKU), Stripe payments, three languages and four currencies.",[515,516,517],"Full-stack","E-commerce","SEO","2024","GRzyokXwmUi2DOm7WAVpm-O0z31wKRm5HR2hvGStbTQ",{"id":521,"title":522,"body":523,"client":151,"cover":551,"description":142,"extension":152,"featured":153,"links":670,"meta":671,"navigation":156,"order":672,"path":673,"role":317,"seo":674,"slug":675,"stack":676,"stem":680,"summary":681,"tags":682,"thumb":624,"year":176,"__hash__":684},"projects_en\u002Fprojects\u002Fen\u002Ffotki.md","Part photos with AI background removal",{"type":8,"value":524,"toc":662},[525,527,530,533,536,540,543,546,552,559,563,570,585,588,592,595,613,618,626,629,633,636,643,646,648],[11,526,14],{"id":13},[16,528,529],{},"Allegro requires a white background for new items. The old photo tool, wired into the first version of Stacja, had no AI. It cut the background by colour: it guessed where the background and similar shades were and removed that area. To make it work, the staff had to overexpose the photos, because only a bright background could be cut out.",[16,531,532],{},"The result was poor. Black parts came out grey, and with similar colours the tool cut too much or too little.",[16,534,535],{},"I wrote a new tool from scratch, \"Fotki\". It runs mainly on Python with the CustomTkinter library, plus Node.js and HTML. It replaced colour-based cutting with models that recognise the object itself, so overexposing the photos was no longer needed.",[11,537,539],{"id":538},"a-phone-instead-of-a-dslr","A phone instead of a DSLR",[16,541,542],{},"Photos were meant to be taken with a DSLR at first. I showed management that modern phones shoot at a very good level and are far handier than a big DSLR. I won them over to the phone and built the whole process around it.",[16,544,545],{},"To speed up development, I didn't build separate Android and iOS apps. The Python program starts a local Node server and shows a QR code. A worker scans it with their phone and opens a page in the browser, the same one on Android and iOS.",[16,547,548],{},[25,549],{"alt":550,"height":28,"src":551,"width":30},"The \"Fotki\" window in Python, with a button that shows the QR code to connect a phone.","\u002Fimages\u002Fprojects\u002Ffotki\u002Fglowna.webp",[16,553,554,555,558],{},"I build the program to an ",[406,556,557],{},"exe",". Launched on its own, it requires a login. Launched from Stacja, it logs in the current user straight away, so the photographer doesn't enter a password twice.",[11,560,562],{"id":561},"photos-from-the-browser","Photos from the browser",[16,564,565,566,569],{},"After scanning the QR code, a simple page opens on the phone. The worker scans the part's barcode, the same one shown in the part details and on the printed label, or types the ",[406,567,568],{},"id"," by hand. The program checks whether the part is in the database and whether it already has photos. If photos exist, they appear right away in the Python window.",[16,571,572,243,580],{},[25,573],{"alt":574,"height":575,"src":576,"width":577,"className":578},"The browser version: scan the part barcode or type the part id.",1612,"\u002Fimages\u002Fprojects\u002Ffotki\u002Fmobile-main.webp",790,[579],"phone-shot",[25,581],{"alt":582,"height":575,"src":583,"width":577,"className":584},"Once the part matches: buttons to take more photos and delete the last one.","\u002Fimages\u002Fprojects\u002Ffotki\u002Fmobile-skan.webp",[579],[16,586,587],{},"From the phone you take more photos or delete the last one. The limit per listing is 16 photos, so the program shows a notice once you reach it.",[11,589,591],{"id":590},"background-removal-on-the-gpu","Background removal on the GPU",[16,593,594],{},"A photo from the phone goes to the program, PyTorch moves it to the graphics card, and the model removes the background. I keep the models loaded in the card's memory, so a single photo is processed in about 0.8 seconds.",[16,596,597,598,601,602,601,605,608,609,612],{},"There are four models to choose from: ",[406,599,600],{},"Remover",", ",[406,603,604],{},"BiRefNet",[406,606,607],{},"BiRefNet-Lite"," and ",[406,610,611],{},"RMBG-2.0",". Each handles tricky shapes and colours differently, so the photographer picks the one that cuts a given part best.",[112,614,615],{},[16,616,617],{},"A single photo is processed in about 0.8 seconds, and black parts finally come out black, with no overexposing.",[16,619,620],{},[25,621],{"alt":622,"height":623,"src":624,"width":625},"The editing window in Python: a photo with the background removed, with rotation and cropping.",843,"\u002Fimages\u002Fprojects\u002Ffotki\u002Fzdjecie.webp",1600,[16,627,628],{},"In the window you rotate and crop the photo. Photos also crop automatically, so even long parts keep the minimum dimensions required to list on the marketplaces.",[11,630,632],{"id":631},"locations","Locations",[16,634,635],{},"The program suggests where to put a given part in the warehouse. That way the worker taking photos sorts the parts at the same time, instead of leaving it for a separate step.",[16,637,638],{},[25,639],{"alt":640,"height":641,"src":642,"width":625},"A warehouse location suggestion for the part being photographed.",906,"\u002Fimages\u002Fprojects\u002Ffotki\u002Flokalizacje.webp",[16,644,645],{},"Confirming sends the photos to the server, from where they land on the listings.",[11,647,120],{"id":119},[72,649,650,653,656,659],{},[42,651,652],{},"Background removal on the GPU with four AI models, one photo in about 0.8 seconds, no overexposing, so black parts finally come out black.",[42,654,655],{},"A phone instead of a DSLR: photos through the browser and a QR code, no dedicated app, the same on Android and iOS.",[42,657,658],{},"Automatic cropping to the marketplaces' minimum dimensions, and a notice once the 16-photo limit is reached.",[42,660,661],{},"Tied into Stacja: login carries over automatically, and the location hint lets workers sort parts while shooting.",{"title":142,"searchDepth":143,"depth":143,"links":663},[664,665,666,667,668,669],{"id":13,"depth":146,"text":14},{"id":538,"depth":146,"text":539},{"id":561,"depth":146,"text":562},{"id":590,"depth":146,"text":591},{"id":631,"depth":146,"text":632},{"id":119,"depth":146,"text":120},[],{},4,"\u002Fprojects\u002Fen\u002Ffotki",{"title":522,"description":142},"fotki",[163,164,677,678,679],"Node.js","PyTorch","HTML \u002F JS","projects\u002Fen\u002Ffotki","A tool for photographing parts with automatic background removal on the GPU. Four AI models (Remover, BiRefNet, BiRefNet-Lite, RMBG-2.0), photos taken from a phone through the browser and a QR code, no dedicated app. Part of the Stacja ecosystem.",[329,683,174],"Computer Vision","8zyySAloIKkxQ13QIXBSOtMy16NmekkuwzwqeZZ1B6g",{"id":686,"title":687,"body":688,"client":151,"cover":1205,"description":142,"extension":152,"featured":156,"links":1206,"meta":1207,"navigation":156,"order":1208,"path":1209,"role":1210,"seo":1211,"slug":1212,"stack":1213,"stem":1219,"summary":1220,"tags":1221,"thumb":782,"year":176,"__hash__":1223},"projects_en\u002Fprojects\u002Fen\u002Fstacja-erp-wms.md","Stacja 2.0 ERP\u002FWMS system",{"type":8,"value":689,"toc":1181},[690,692,695,698,701,706,710,713,720,723,743,746,749,752,756,759,776,783,794,799,802,806,809,829,835,838,863,869,872,874,877,881,884,888,891,897,904,933,936,941,945,954,987,991,998,1004,1008,1011,1028,1034,1038,1041,1045,1048,1074,1078,1081,1101,1107,1111,1114,1138,1144,1148,1151,1155,1158,1178],[11,691,14],{"id":13},[16,693,694],{},"When I joined the company, it was running an outdated, off-the-shelf program for managing vehicle dismantling. It worked for about a year, with its bugs, but you could get the job done on it. It was slow, though, looked archaic, and packages had to be added with a separate tool.",[16,696,697],{},"Before Stacja existed, I wrote a few standalone programs meant simply to work alongside that old system and speed the work up. A package-adding tool, the packer and a photo-capture app, each one patched a different bottleneck. All of them were built in Nuxt and Electron.",[16,699,700],{},"The turning point was Allegro. It started requiring GPSR data to publish and edit listings, and the old program didn't support it. The company simply stopped listing and lost one of its two sales channels. That was the key reason to stop patching the old system and build my own from scratch.",[112,702,703],{},[16,704,705],{},"That's how Stacja came about. I built the MVP in two weeks to unblock Allegro listings as fast as possible. Because the earlier tools were already in Nuxt and Electron, folding them into Stacja 2.0 went smoothly.",[11,707,709],{"id":708},"architecture","Architecture",[16,711,712],{},"Today Stacja runs the company's whole flow, from the warehouse, through sales, packing and shipping, to deliveries and warehouse documents.",[16,714,715,716,719],{},"The program runs on Electron, so ",[45,717,718],{},"every machine spins up its own local server",". Photos and the database are kept locally, which means the program keeps working even when the internet goes down. The whole UI also has a dark mode, so everyone works the way they prefer.",[16,721,722],{},"Stacja is also part of a larger ecosystem of tools.",[72,724,725,731,737],{},[42,726,727,730],{},[45,728,729],{},"AWP",", a separate auction tool that supplies the won packages.",[42,732,733,736],{},[45,734,735],{},"Photos",", an app for taking pictures of parts.",[42,738,739,742],{},[45,740,741],{},"Stacja for scanners",", an app for put-away and picking.",[16,744,745],{},"This is the core warehouse module.",[369,747,735],{"id":748},"photos",[16,750,751],{},"From within Stacja you can also launch the \"photos\" program for taking pictures of parts. Like adding and the packer, it was built before Stacja and still runs as a separate module, which I'll describe separately.",[11,753,755],{"id":754},"warehouse","Warehouse",[16,757,758],{},"The warehouse is the heart of the program. We have a choice of tables to read from and several predefined views. Every setting is saved per user, and everyone arranges the columns their own way.",[72,760,761,764,767,770,773],{},[42,762,763],{},"Reorder columns and hide the ones you don't need.",[42,765,766],{},"Filter each column individually.",[42,768,769],{},"Sort and resize.",[42,771,772],{},"Preview a part's photo on row click (bottom-left corner).",[42,774,775],{},"Export everything with one button.",[16,777,778],{},[25,779],{"alt":780,"height":781,"src":782,"width":30},"The warehouse table: 22,000+ parts in one view with configurable columns and photo previews.",1442,"\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fmagazyn.webp",[16,784,785,786,789,790,793],{},"We currently have over 22,000 parts. Rendering that many rows with a plain ",[406,787,788],{},"v-for"," could freeze the whole program, so I used ",[406,791,792],{},"Clusterize",". The DOM only ever holds about 50 rows, the ones currently on screen, and the rest are drawn on the fly as you scroll.",[112,795,796],{},[16,797,798],{},"22,000+ parts in one view, yet the browser only ever paints ~50 rows at a time. The list stays smooth and filtering feels instant.",[16,800,801],{},"Here you can also select products, for example the ones that have been in stock for two years, and mark them down by a set percentage.",[369,803,805],{"id":804},"product-details","Product details",[16,807,808],{},"Every product has a rich modal that gathers all its data in one place.",[72,810,811,814,817,820,823,826],{},[42,812,813],{},"Name, price, mounting side, factory number, colour, mileage, condition and locations.",[42,815,816],{},"Quantity received, issued and remaining, plus the warehousing cost.",[42,818,819],{},"Dimensions and weight (length, width, height).",[42,821,822],{},"Barcode (used in the scanner version).",[42,824,825],{},"A description and an internal note, for example about a defect.",[42,827,828],{},"In the corner, who added the part and when.",[16,830,831],{},[25,832],{"alt":833,"height":781,"src":834,"width":30},"Product details with photos that can be reordered, rotated and cropped. Edits show up on the marketplaces immediately.","\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fczesc-zdjecia.webp",[16,836,837],{},"There are tabs on the right.",[72,839,840,845,851,857],{},[42,841,842,844],{},[45,843,735],{},", every picture of the part. Reorder, crop, rotate and delete.",[42,846,847,850],{},[45,848,849],{},"Fits",", vehicle fitment. The more fitments, the easier the part is to find.",[42,852,853,856],{},[45,854,855],{},"Numbers",", the part's other numbers (several identical parts can carry different ones).",[42,858,859,862],{},[45,860,861],{},"History",", the part's full timeline, meaning adding, photos, put-away, sale, picking and packing.",[16,864,865],{},[25,866],{"alt":867,"height":781,"src":868,"width":30},"The fitment tab: the donor car plus every model the part fits, drawn from a database of 70,000+ vehicles.","\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fczesc-dopasowanie.webp",[16,870,871],{},"Once changes are saved (photos, descriptions and parameters), they go straight to the listings on the marketplaces.",[369,873,632],{"id":631},[16,875,876],{},"The company has several warehouses. This tab lets you check what's sitting on a given shelf.",[369,878,880],{"id":879},"stocktaking","Stocktaking",[16,882,883],{},"Plain stocktaking. You can check which locations have already been counted and with what result, how many parts came out over or short.",[11,885,887],{"id":886},"adding","Adding",[16,889,890],{},"Adding was built before Stacja, as a standalone tool for the old program, and later became part of Stacja 2.0. An AI agent works here, built on the same pattern I use for client work. The LLM is the brain, the agent gets tools, meaning access to our database, scraping and price comparison, and decides on its own what to use to fill in the fields required to add a part.",[16,892,893],{},[25,894],{"alt":895,"height":781,"src":896,"width":30},"Adding a part: the AI agent fills in cross-references, the name and the fitments, then suggests a price.","\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fdodawanie-pakiet.webp",[16,898,899,900,903],{},"A worker only needs the ",[406,901,902],{},"OE"," number read off the part. Click the lightbulb and the agent runs in order.",[39,905,906,912,918,927],{},[42,907,908,911],{},[45,909,910],{},"Checks the database."," If the part was already in stock, it fills in the fields we have.",[42,913,914,917],{},[45,915,916],{},"Runs scraping."," It looks for the name, cross-reference numbers, vehicle fitments and prices across many sites at once.",[42,919,920,243,923,926],{},[45,921,922],{},"Cleans the data up with the LLM.",[406,924,925],{},"DeepSeek"," maps scraped names onto our dictionary and, from a database of 70,000+ vehicles, picks only the matching models. I narrow the make first so the model's context doesn't overflow.",[42,928,929,932],{},[45,930,931],{},"Analyses prices."," Based on the scraped prices it suggests one of its own, and the moment the part is added a barcode label prints.",[16,934,935],{},"A few things the agent won't do for the worker. Dimensions have to be measured and entered by hand, because they're in no source and the packer needs them later. The description is built from predefined templates depending on the filled-in fields, but you can add your own text. Buttons to the marketplaces let the worker quickly check whether the agent's suggested price holds up against the market. If the part was already in the database, its current price shows. You can also copy a row, reprint the label, and update an old, stale price.",[112,937,938],{},[16,939,940],{},"Thanks to the agent I made adding parts five times faster and took the manual page-hunting off people's plates.",[369,942,944],{"id":943},"package-warehouse","Package warehouse",[16,946,947,948,950,951,953],{},"Stock comes from packages won at auctions, handled by a separate program (",[406,949,729],{},"). Once a day Stacja pulls the won packages and processes them, extracting the ",[406,952,902],{}," numbers and quantities. From there a package follows a document trail.",[39,955,956,963,970,984],{},[42,957,958,959,962],{},"It lands in the package warehouse and ",[45,960,961],{},"waits to be received",", you can't add from it yet.",[42,964,965,966,969],{},"Receiving generates a ",[406,967,968],{},"PZ"," document → only now can the contents be added.",[42,971,972,973,976,977,608,980,983],{},"Once everything is added, the worker ",[45,974,975],{},"closes the package"," → ",[406,978,979],{},"RW",[406,981,982],{},"PW"," are created.",[42,985,986],{},"The part moves on, to photos (the \"photos\" program) and put-away (Stacja for scanners).",[11,988,990],{"id":989},"sales-shipping","Sales & shipping",[16,992,993,994,997],{},"In Stacja you can manage each listing individually and by hand, meaning its state, quantity and price. The program is also fully connected to the OMS platforms (previously Baselinker, now SellRocket). When an order comes in, Stacja automatically removes that part from stock and edits the remaining listings so the ",[45,995,996],{},"same part can't be sold twice",". Orders flow to a \"to pick\" list and to the scanners, where workers pick them off the floor.",[16,999,1000],{},[25,1001],{"alt":1002,"height":781,"src":1003,"width":30},"The \"to pick\" list: orders pulled automatically from the OMS, ready to pick and visible on the scanners.","\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fdo-zebrania.webp",[369,1005,1007],{"id":1006},"packer","Packer",[16,1009,1010],{},"The packer was also built before Stacja, as a separate program in Nuxt and Electron, so wiring it in was no trouble. Once the order barcode is scanned, packing begins.",[39,1012,1013,1016,1019,1025],{},[42,1014,1015],{},"The program suggests how to arrange the parts (when there are several) and which box to use, based on the dimensions from adding.",[42,1017,1018],{},"The worker enters the finished parcel's dimensions.",[42,1020,1021,1022,1024],{},"Shipping options appear, with prices computed through each contracted courier's ",[406,1023,167],{},", either the one forced by the order, or all of them with the cheapest selected automatically.",[42,1026,1027],{},"The label prints and the delivery method lands in the OMS.",[16,1029,1030],{},[25,1031],{"alt":1032,"height":781,"src":1033,"width":30},"The packer: box suggestion based on the part's dimensions, with the cheapest shipping option selected automatically.","\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fpakowacz.webp",[369,1035,1037],{"id":1036},"goods-issued","Goods issued",[16,1039,1040],{},"Every goods issue is either a part sale with a date or a scrapping. Having them in one place makes it clear exactly what left the warehouse and when.",[369,1042,1044],{"id":1043},"warehouse-documents","Warehouse documents",[16,1046,1047],{},"Documents are generated automatically as work happens.",[72,1049,1050,1055,1062,1068],{},[42,1051,1052,1054],{},[406,1053,968],{},", when a package is received.",[42,1056,1057,608,1059,1061],{},[406,1058,979],{},[406,1060,982],{},", when a package is closed.",[42,1063,1064,1067],{},[406,1065,1066],{},"WZ",", on a sale, once the invoice or receipt is issued.",[42,1069,1070,1071,1073],{},"A ",[406,1072,1066],{}," correction, on a return.",[11,1075,1077],{"id":1076},"reports-administration","Reports & administration",[16,1079,1080],{},"There are three different reports.",[72,1082,1083,1089,1095],{},[42,1084,1085,1088],{},[45,1086,1087],{},"Periodic",", emailed daily. It shows sales, parts added and photos taken, plotted over time, compared with other days and broken down by worker.",[42,1090,1091,1094],{},[45,1092,1093],{},"Overall",", everything since the company started. All the packages, how much of each has sold and whether it's already profitable.",[42,1096,1097,1100],{},[45,1098,1099],{},"Part age",", shows how old the stock is. If too many old parts start piling up, it's a sign to run a promotion.",[16,1102,1103],{},[25,1104],{"alt":1105,"height":781,"src":1106,"width":30},"The overall report: package profitability and key metrics since day one.","\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fraport-ogolny.webp",[369,1108,1110],{"id":1109},"admin-panel","Admin panel",[16,1112,1113],{},"The admin panel shows who logged in and when, how long they work and what they're doing right now. Time is broken down by activity, which makes planning easier.",[72,1115,1116,1121,1127,1133],{},[42,1117,1118,1120],{},[45,1119,887],{},", how long the next package of similar parts will take.",[42,1122,1123,1126],{},[45,1124,1125],{},"Packing",", whether to assign more people to keep up with shipments.",[42,1128,1129,1132],{},[45,1130,1131],{},"Put-away",", who put away how much.",[42,1134,1135,1137],{},[45,1136,735],{},", the progress of taking pictures.",[16,1139,1140],{},[25,1141],{"alt":1142,"height":781,"src":1143,"width":30},"The admin panel: employee work time and status broken down by activity.","\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fpanel-admina.webp",[369,1145,1147],{"id":1146},"news-feed","News feed",[16,1149,1150],{},"After logging in, a worker lands on the news feed. It's the place for posts from people who want to pass something on to the rest of the team, meaning notes, decisions and current information. Posts take comments and likes, and at the top you can see right away how many packages are still waiting to be closed.",[11,1152,1154],{"id":1153},"summary","Summary",[16,1156,1157],{},"A lot came together in this one project, from scraping, through LLM calls, pulling orders from the OMS platforms, warehouse documents and packing with courier selection, to migrating data out of the old system. The main outcomes are below.",[72,1159,1160,1163,1166,1172,1175],{},[42,1161,1162],{},"The legacy stack replaced by one system, with sales unblocked by an MVP in two weeks.",[42,1164,1165],{},"5x more parts added thanks to the AI agent, with no manual page-hunting.",[42,1167,1168,1169,1171],{},"A 22,000-part table that scrolls smoothly thanks to ",[406,1170,792],{},".",[42,1173,1174],{},"Sales fully automated, with stock and orders synced to the OMS and packing that picks the box and the cheapest shipping on its own.",[42,1176,1177],{},"Works offline thanks to the local server on Electron.",[16,1179,1180],{},"The biggest lesson from this project is that the real value wasn't standing up a system, it was plugging AI in exactly where it removed the dullest, most repetitive work, and doing it so that the team trusts it in production.",{"title":142,"searchDepth":143,"depth":143,"links":1182},[1183,1184,1187,1192,1195,1200,1204],{"id":13,"depth":146,"text":14},{"id":708,"depth":146,"text":709,"children":1185},[1186],{"id":748,"depth":143,"text":735},{"id":754,"depth":146,"text":755,"children":1188},[1189,1190,1191],{"id":804,"depth":143,"text":805},{"id":631,"depth":143,"text":632},{"id":879,"depth":143,"text":880},{"id":886,"depth":146,"text":887,"children":1193},[1194],{"id":943,"depth":143,"text":944},{"id":989,"depth":146,"text":990,"children":1196},[1197,1198,1199],{"id":1006,"depth":143,"text":1007},{"id":1036,"depth":143,"text":1037},{"id":1043,"depth":143,"text":1044},{"id":1076,"depth":146,"text":1077,"children":1201},[1202,1203],{"id":1109,"depth":143,"text":1110},{"id":1146,"depth":143,"text":1147},{"id":1153,"depth":146,"text":1154},"\u002Fimages\u002Fprojects\u002Fstacja-erp-wms\u002Fcover.webp",[],{},1,"\u002Fprojects\u002Fen\u002Fstacja-erp-wms","AI Engineer & Full-Stack Developer",{"title":687,"description":142},"stacja-erp-wms",[321,1214,163,322,1215,166,1216,1217,1218],"PostgreSQL","Electron","AI Agents","SellRocket","Baselinker","projects\u002Fen\u002Fstacja-erp-wms","A program that manages the warehouse, sales, packing, shipping, deliveries and documents. I built the MVP in 2 weeks, then came AI agents for pricing and part matching, plus multi-channel sales sync.",[515,1222,329],"ERP \u002F WMS","5s8-D5wk4Gkhpg6G8CGE-Suy8XIbIeiMBvF6tYkgoGc",{"id":1225,"title":741,"body":1226,"client":151,"cover":1371,"description":142,"extension":152,"featured":153,"links":1372,"meta":1373,"navigation":156,"order":1374,"path":1375,"role":1376,"seo":1377,"slug":1378,"stack":1379,"stem":1383,"summary":1384,"tags":1385,"thumb":175,"year":176,"__hash__":1389},"projects_en\u002Fprojects\u002Fen\u002Fstacja-mobilna.md",{"type":8,"value":1227,"toc":1362},[1228,1230,1233,1247,1250,1255,1259,1262,1274,1277,1281,1284,1291,1296,1300,1303,1315,1317,1320,1332,1336,1339,1346,1348],[11,1229,14],{"id":13},[16,1231,1232],{},"Stacja for scanners is the version of Stacja built for warehouse scanners, phones with a built-in barcode reader. The interface is touch-first. The keyboard hides and can be locked, because you only need it to log in. You sign in to every program in the Stacja ecosystem with the same account.",[16,1234,1235,243,1242],{},[25,1236],{"alt":1237,"className":1238,"height":1239,"src":1240,"width":1241},"The login screen, one shared account for the whole Stacja ecosystem.",[579],1682,"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Flogin.webp",768,[25,1243],{"alt":1244,"className":1245,"height":1239,"src":1246,"width":1241},"The home screen: tiles for the warehouse actions.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Fglowna.webp",[16,1248,1249],{},"After logging in you see tiles with every option, meaning check a part, picking, putting away, stocktaking, where to put it, shelf info and pallet locations. A button in the bottom-left locks the keyboard, so instead of typing anything, you scan the code on a part or a location.",[112,1251,1252],{},[16,1253,1254],{},"The keyboard only pops up at login. Everything else is touch and scan, because on a scanner one hand and pace are what count.",[11,1256,1258],{"id":1257},"checking-a-part","Checking a part",[16,1260,1261],{},"You scan the barcode on the label stuck to the part, the one printed when it was added. You see the data straight away: the EI number, photos, price, available quantity, locations and the OE number.",[16,1263,1264,243,1269],{},[25,1265],{"alt":1266,"className":1267,"height":1239,"src":1268,"width":1241},"Checking a part after scanning the label: EI number, photos, price, quantity, locations and OE number.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Fsprawdz-czesc.webp",[25,1270],{"alt":1271,"className":1272,"height":1239,"src":1273,"width":1241},"Part tabs: the same parts under one number, plus the full part history.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Fsprawdz-czesc-2.webp",[16,1275,1276],{},"The tabs add two more things. \"The same parts\" shows items under the same number, so when something is wrong with one, you grab another. The part history walks through its whole path: added, put away, listed and sold.",[11,1278,1280],{"id":1279},"picking","Picking",[16,1282,1283],{},"The \"To pick\" list holds products someone bought, which Stacja took off stock. It's the same picking as in Stacja. You see the part name, its id, how many to bring from the warehouse and the location. You confirm a pick by scanning the part's id.",[16,1285,1286],{},[25,1287],{"alt":1288,"className":1289,"height":1239,"src":1290,"width":1241},"The \"To pick\" list: name, id, quantity and location, ordered along the warehouse route.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Fdo-zebrania.webp",[112,1292,1293],{},[16,1294,1295],{},"The list is ordered the way a worker walks the warehouse, so they pick the whole order along the way instead of circling back several times.",[11,1297,1299],{"id":1298},"putting-away-and-where-to-put-it","Putting away and where to put it",[16,1301,1302],{},"When putting away, you scan the location code and then the code of the part you're placing there. \"Where to put it\" works the other way round: you scan a part and see where items with the same OE number and the same name already sit. That keeps the warehouse tidy and similar parts in one place.",[16,1304,1305,243,1310],{},[25,1306],{"alt":1307,"className":1308,"height":1239,"src":1309,"width":1241},"Putting away: scan the location, then scan the part.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Fodkladanie.webp",[25,1311],{"alt":1312,"className":1313,"height":1239,"src":1314,"width":1241},"Where to put it: after scanning a part, you see where the same ones already are.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Fgdzie-odlozyc.webp",[11,1316,880],{"id":879},[16,1318,1319],{},"You create a stocktake and scan parts to check what should be in a given location. Scanning each item clears the list. Once you pick a location, you see every part to scan, and for several items a counter shows how many you've already scanned.",[16,1321,1322,243,1327],{},[25,1323],{"alt":1324,"className":1325,"height":1239,"src":1326,"width":1241},"Stocktaking: creating a count and scanning parts in a location.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Finwentaryzacja.webp",[25,1328],{"alt":1329,"className":1330,"height":1239,"src":1331,"width":1241},"A chosen location: the list of parts to scan with an item counter.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Finwentaryzacja-2.webp",[11,1333,1335],{"id":1334},"shelf-info","Shelf info",[16,1337,1338],{},"You scan a shelf and see exactly what's on it.",[16,1340,1341],{},[25,1342],{"alt":1343,"className":1344,"height":1239,"src":1345,"width":1241},"Shelf info: the full contents after scanning the shelf code.",[579],"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Finfo-o-polce.webp",[11,1347,120],{"id":119},[72,1349,1350,1353,1356,1359],{},[42,1351,1352],{},"The whole warehouse loop on a scanner: checking, picking, putting away, stocktaking and a shelf view in one app.",[42,1354,1355],{},"One-handed work: the keyboard only for login, everything else touch and a code scan.",[42,1357,1358],{},"Picking ordered along the warehouse route, so workers don't backtrack down the same aisles.",[42,1360,1361],{},"Tied into Stacja by the same account and the same data, including orders from the OMS systems.",{"title":142,"searchDepth":143,"depth":143,"links":1363},[1364,1365,1366,1367,1368,1369,1370],{"id":13,"depth":146,"text":14},{"id":1257,"depth":146,"text":1258},{"id":1279,"depth":146,"text":1280},{"id":1298,"depth":146,"text":1299},{"id":879,"depth":146,"text":880},{"id":1334,"depth":146,"text":1335},{"id":119,"depth":146,"text":120},"\u002Fimages\u002Fprojects\u002Fstacja-mobilna\u002Fcover.webp",[],{},6,"\u002Fprojects\u002Fen\u002Fstacja-mobilna","Full-Stack Engineer",{"title":741,"description":142},"stacja-mobilna",[321,1380,1381,1382],"Vue","PWA","Barcode scanning","projects\u002Fen\u002Fstacja-mobilna","The version of Stacja built for warehouse scanners, phones with a built-in barcode reader. The whole warehouse loop in one app, from checking parts, through picking along the warehouse route and putting away, to stocktaking and shelf lookup. The keyboard shows up only at login. Everything else is touch and scan.",[1386,1387,1388],"Mobile","WMS","UX","ENX0nFQGA87QYkU2AIPI9AzUha9YwFkymUr2PIc3sOg",1783967623784]