[{"data":1,"prerenderedAt":231},["ShallowReactive",2],{"i-lucide:arrow-right":3,"i-simple-icons:linkedin":8,"i-simple-icons:github":11,"i-lucide:file-text":13,"i-lucide:arrow-up":15,"project-pl-leathermaster":17,"projects-pl":209,"i-lucide:lock":215,"i-lucide:credit-card":217,"i-lucide:arrow-up-right":219,"i-lucide:chevron-left":221,"i-lucide:chevron-right":223,"i-lucide:layout-dashboard":225,"i-lucide:key-round":227,"i-lucide:git-branch":229},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9,"hidden":10},"\u003Cpath fill=\"currentColor\" d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.06 2.06 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065m1.782 13.019H3.555V9h3.564zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0z\"\u002F>",true,{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":12},"\u003Cpath fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":14},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\"\u002F>\u003Cpath d=\"M14 2v5a1 1 0 0 0 1 1h5M10 9H8m8 4H8m8 4H8\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":16},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m5 12l7-7l7 7m-7 7V5\"\u002F>",{"id":18,"title":19,"body":20,"description":111,"extension":124,"facts":125,"gallery":138,"highlights":163,"image":178,"meta":181,"navigation":10,"order":182,"path":183,"role":184,"screens":185,"seo":197,"stack":198,"stem":205,"summary":206,"url":105,"year":207,"__hash__":208},"projects_pl\u002Fprojekty\u002Fleathermaster.md","LeatherMaster",{"type":21,"value":22,"toc":110},"minimark",[23,28,32,36,39,43,48,56,59,63,66,73,77,84,88,95,99],[24,25,27],"h2",{"id":26},"zadanie","Zadanie",[29,30,31],"p",{},"Zakład z Dąbrówki pod Poznaniem potrzebował strony, która pokaże usługi i metamorfozy przed i po, przyjmie zapisy na szkolenia z płatnością online i da się edytować bez programisty.",[24,33,35],{"id":34},"co-zrobiłem","Co zrobiłem",[29,37,38],{},"Wszystko sam. Rozmawiałem z właścicielem, zaprojektowałem stronę, napisałem backend w Laravelu, front w Blade i Tailwindzie oraz panel w Livewire, podpiąłem płatności i ustawiłem CI\u002FCD.",[24,40,42],{"id":41},"szczegóły","Szczegóły",[44,45,47],"h3",{"id":46},"zapisy-i-płatności","Zapisy i płatności",[29,49,50,51,55],{},"Jednym zapisem można zarezerwować kilka miejsc i podać nazwisko każdego uczestnika. Rezerwacja ma licznik czasu i po nim wygasa, a po opłaceniu klient dostaje plik ",[52,53,54],"code",{},".ics"," do kalendarza. Dane firmy wypełniają się same po wpisaniu NIP-u. Biorę je z białej listy VAT i trzymam w cache przez 24 godziny, bo API ma dzienny limit zapytań.",[29,57,58],{},"Płatności idą przez ING Pay, BLIK-iem albo przelewem. Webhook sprawdza podpis, a odrzuconą lub wygasłą płatność klient może ponowić. Sandbox w kilku miejscach zachowuje się inaczej, niż mówi dokumentacja, więc zapisałem to w README.",[44,60,62],{"id":61},"panel","Panel",[29,64,65],{},"Usunięte rzeczy leżą w koszu panelu przez 30 dni i można je przywrócić. Każdy rekord ma historię zmian, ekrany wyszukuje się pod ⌘K, a na telefonie menu jest w dolnym pasku.",[29,67,68],{},[69,70],"img",{"alt":71,"src":72},"Pulpit panelu LeatherMaster: najbliższe terminy szkoleń z liczbą zajętych miejsc i ostatnie zapisy ze statusem płatności (dane przykładowe)","\u002Fimages\u002Fprojects\u002Fleathermaster-panel.webp",[44,74,76],{"id":75},"bezpieczeństwo","Bezpieczeństwo",[29,78,79,80,83],{},"Do panelu loguje się passkeyem i kodem 2FA. Identyfikatory passkeyów podpisuję osobnym sekretem, żeby zmiana ",[52,81,82],{},"APP_KEY"," ich nie unieważniła.",[44,85,87],{"id":86},"seo","SEO",[29,89,90,91,94],{},"Sitemap i dane strukturalne generują się same. Gdy zmieni się adres podstrony, stary przekierowuje 301 na nowy, a wyłączona strona odpowiada 503 z nagłówkiem ",[52,92,93],{},"Retry-After",".",[24,96,98],{"id":97},"efekt","Efekt",[29,100,101,102,109],{},"Strona działa pod adresem ",[103,104,108],"a",{"href":105,"rel":106},"https:\u002F\u002Fwww.leathermaster.pl",[107],"nofollow","leathermaster.pl",". Właściciel sam zmienia treści i dodaje terminy szkoleń, a zapisy obsługuje w panelu bez mojej pomocy.",{"title":111,"searchDepth":112,"depth":112,"links":113},"",2,[114,115,116,123],{"id":26,"depth":112,"text":27},{"id":34,"depth":112,"text":35},{"id":41,"depth":112,"text":42,"children":117},[118,120,121,122],{"id":46,"depth":119,"text":47},3,{"id":61,"depth":119,"text":62},{"id":75,"depth":119,"text":76},{"id":86,"depth":119,"text":87},{"id":97,"depth":112,"text":98},"md",[126,129,132,135],{"value":127,"label":128},"BLIK","płatność za szkolenie przez ING Pay",{"value":130,"label":131},"Passkey","logowanie do panelu, do tego 2FA",{"value":133,"label":134},"250+","testów przy każdym pushu",{"value":136,"label":137},"30 dni","na przywrócenie usuniętej treści",[139,144,149,153,158],{"src":140,"alt":141,"title":142,"text":143},"\u002Fimages\u002Fprojects\u002Fleathermaster-training.webp","Strona szkolenia LeatherMaster: zdjęcie z zajęć, najbliższy termin, cena za osobę i przycisk zapisu","Strona szkolenia.","Najbliższy termin, cena za osobę i forma zajęć. Stąd klient przechodzi do zapisów.",{"src":145,"alt":146,"title":147,"text":148},"\u002Fimages\u002Fprojects\u002Fleathermaster-booking.webp","Formularz zapisu na szkolenie w trzech krokach, z podsumowaniem terminu i ceny obok","Zapis na termin.","Trzy kroki, czyli dane, płatność i potwierdzenie. Dane firmy uzupełniają się po wpisaniu NIP-u.",{"src":72,"alt":150,"title":151,"text":152},"Pulpit panelu LeatherMaster: najbliższe terminy szkoleń i ostatnie zapisy ze statusem płatności (dane przykładowe)","Pulpit.","Po zalogowaniu właściciel widzi najbliższe terminy, wolne miejsca i to, czy ostatnie zapisy są opłacone.",{"src":154,"alt":155,"title":156,"text":157},"\u002Fimages\u002Fprojects\u002Fleathermaster-terms.webp","Kalendarz terminów w panelu z listą terminów grupowych i indywidualnych oraz przełącznikiem widoczności","Terminy i zapisy.","Kalendarz z terminami grupowymi i indywidualnymi. Każdy termin chowa się ze strony jednym przełącznikiem.",{"src":159,"alt":160,"title":161,"text":162},"\u002Fimages\u002Fprojects\u002Fleathermaster-editor.webp","Edycja szkolenia w panelu: edytor opisu, zdjęcie z ustawianym kadrem i przycisk zapisu","Edycja szkolenia.","Opis w edytorze, zdjęcie z ustawianym kadrem i zapis skrótem ⌘S.",[164,167,171,174],{"icon":165,"title":47,"text":166},"lucide:credit-card","Rezerwacja miejsc na szkolenia i płatność BLIK przez ING Pay, z weryfikacją webhooków i ponowieniem płatności.",{"icon":168,"title":169,"text":170},"lucide:layout-dashboard","Panel w Livewire 4","Przejścia bez przeładowań, wyszukiwarka ⌘K, historia zmian i kosz z przywracaniem.",{"icon":172,"title":76,"text":173},"lucide:key-round","Logowanie passkey i 2FA, Content Security Policy, limity formularzy i Turnstile.",{"icon":175,"title":176,"text":177},"lucide:git-branch","CI\u002FCD","Testy i analiza statyczna przy każdym pushu, deploy z wersją CalVer i smoke testem.",{"src":179,"alt":180},"\u002Fimages\u002Fprojects\u002Fleathermaster.webp","Strona główna LeatherMaster: nagłówek z ofertą renowacji skóry i przyciskiem kontaktu",{},"1","\u002Fprojekty\u002Fleathermaster","Projekt, backend, frontend, wdrożenie",[186,192],{"label":187,"path":188,"src":179,"mobile":189,"alt":180,"title":190,"text":191},"Strona","\u002F","\u002Fimages\u002Fprojects\u002Fleathermaster-mobile.webp","Klient zapisuje się i płaci.","Wybiera termin szkolenia, wpisuje dane i płaci BLIK-iem albo przelewem.",{"label":62,"path":193,"src":72,"mobile":194,"alt":150,"title":195,"text":196},"\u002Fpanel","\u002Fimages\u002Fprojects\u002Fleathermaster-panel-mobile.webp","Właściciel widzi to w panelu.","Zapis trafia na pulpit razem ze statusem płatności, obok najbliższych terminów.",{"title":19,"description":111},[199,200,201,202,203,204],"Laravel 13","Livewire 4","Tailwind CSS 4","MySQL","GitHub Actions","Cloudflare","projekty\u002Fleathermaster","Strona i własny panel CMS dla zakładu renowacji tapicerki skórzanej. Klienci zapisują się na szkolenia i płacą online, a właściciel sam zmienia treści w panelu.","2026","cWkq2EUvqy4eUws6BN33gbeu-Xajd1VspzCtdWf2PIE",[210,211],{"path":183,"title":19,"summary":206},{"path":212,"title":213,"summary":214},"\u002Fprojekty\u002Fportfolio","Portfolio","Ta strona. Dwa języki, statyczny HTML, case study i CV generowane z tych samych danych, a kod napisany razem z Claude Code.",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":216},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M7 11V7a5 5 0 0 1 10 0v4\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":218},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"20\" height=\"14\" x=\"2\" y=\"5\" rx=\"2\"\u002F>\u003Cpath d=\"M2 10h20M6 14h2\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":220},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 7h10v10M7 17L17 7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":222},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m15 18l-6-6l6-6\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":224},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m9 18l6-6l-6-6\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":226},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"7\" height=\"9\" x=\"3\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"5\" x=\"14\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"9\" x=\"14\" y=\"12\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"5\" x=\"3\" y=\"16\" rx=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":228},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z\"\u002F>\u003Ccircle cx=\"16.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":230},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M15 6a9 9 0 0 0-9 9V3\"\u002F>\u003Ccircle cx=\"18\" cy=\"6\" r=\"3\"\u002F>\u003Ccircle cx=\"6\" cy=\"18\" r=\"3\"\u002F>\u003C\u002Fg>",1792789502421]