From fabff18fe9e00e1058f78059d46977f41e2ce01a Mon Sep 17 00:00:00 2001 From: ht Date: Fri, 4 Sep 2026 00:39:40 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20mobil=20uyumlu=20PWA=20=E2=80=94=20mani?= =?UTF-8?q?fest,=20service=20worker,=20gercek=20kurulum,=20filtre=20cekmec?= =?UTF-8?q?esi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - public/manifest.webmanifest (standalone, tr, kisayollar) + 192/512 ve maskable ikonlar + apple-touch-icon; index.html PWA meta etiketleri - public/sw.js: /assets cache-first (hash'li adlar), kabuk network-first, /api ASLA onbellekten (eski ihale gosterilmesin); surum degisince temizlik - main.jsx: SW yalniz uretimde kaydedilir (dev'de HMR ile cakismasin) - Mobil PWA sayfasindaki "Cihaza Yukle" dugmesi sahteydi (setInstalled(true)); artik beforeinstallprompt ile gercek kurulum, standalone algilama, iOS/ Android/masaustu yol tarifi - Kucuk ekranda sol filtre paneli listeyi 72 satir asagi itiyordu: lg altinda "Filtreler" dugmesiyle acilan cekmece; sag panel xl altinda gizli Co-Authored-By: Claude Fable 5.1 --- index.html | 9 +++++- public/icons/apple-touch-icon.png | Bin 0 -> 1508 bytes public/icons/icon-192.png | Bin 0 -> 1603 bytes public/icons/icon-512.png | Bin 0 -> 4573 bytes public/icons/maskable-192.png | Bin 0 -> 1625 bytes public/icons/maskable-512.png | Bin 0 -> 4528 bytes public/manifest.webmanifest | 24 ++++++++++++++++ public/sw.js | 28 +++++++++++++++++++ src/App.jsx | 8 ++++-- src/components/MobilFiltreCekmecesi.jsx | 35 ++++++++++++++++++++++++ src/components/tools/PwaSettings.jsx | 34 ++++++++++++++++++++--- src/main.jsx | 5 ++++ 12 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 public/icons/apple-touch-icon.png create mode 100644 public/icons/icon-192.png create mode 100644 public/icons/icon-512.png create mode 100644 public/icons/maskable-192.png create mode 100644 public/icons/maskable-512.png create mode 100644 public/manifest.webmanifest create mode 100644 public/sw.js create mode 100644 src/components/MobilFiltreCekmecesi.jsx diff --git a/index.html b/index.html index 0921123..56a4b3b 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,14 @@ - + + + + + + + + İhaleist — Akıllı İhale Takip ve Analiz Platformu diff --git a/public/icons/apple-touch-icon.png b/public/icons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4bf28580af7c9c31937da9450fef96546cf81c55 GIT binary patch literal 1508 zcmeAS@N?(olHy`uVBq!ia0vp^TR@nD4M^IaWiw)6U|sF$;uumf=j~npjF?b~wvX@q z)QnG>vj*Z%!kDMH}Tw^r5l!8Rhy4~>*Z_++!FA}>Wb72=t zELZE2MIB9-l(M1)mog^j3eSFaPGw>bi`R-YwVBoDcD}EyGuZ99V{+xEr)T!2t9jl( z@SurNiEAG}LxyTon+REG%A%(}neSJ;ckb}BvdgKQ*z#{(J`38IOaCWVU`cHJNaG!OU}oT9$JzooiBiwqvS{>sytKq_)+35zfwD&m4{C zKHS8vX0jwj%H*=-y4?v*&R%IC!NvSQK>;vI5i)t5{^L~(C$A<82HN@C;%-=F){dm} zwh@sZ>*RaS-|}u>K1W9QS|3*R5ECxOY1U+a3Hr;X>yzQ32t%FUTpvDfH;a7#Y-;`d z%eR`|RuvT;{k`|+k*eMQ`#yDj|NW#p()x$o?ZC~4T8IkrDH-LrTQhDiYvR2dw#hm_ zZ@cVX*WKH8C(M0%ByBZ!SzXn^bAD%TZePB-?9c8i`+n|x_Pe0j(u4v&!(2n*m+3m*5ub_VsBn}Y`5LE{^yMK zTUM{naCfiD@vpDe=GWiOajSWsZf@C=r*hIe7Mj*8TrXGq_RUy2=eKFDm~~TGj<^58 zd-ZkBe_0c>OV@k9nd*Km@(stWjYo|o_DcF*s@?5vw!`?{Qg+AgoAvqaM|(EQF#h<| zp7nRWaP8mVt7_-YM^B3QD{}X(^{vBqHEk87XE&Hc4CZGN5U z?JL)7>bIp$4(^S8d~c89z2&C&+}~Z?xbaz1+^5qn$-;YAC-Y91EACrhD`EZf|LbS} zKOH}M>|9LAG)}2Loq2xIcQm(41snJ3T6PJ>Gp0LPf4lwU&#LX!=S+44Wp@9F?ESIu zw_#=B*V)roZ#_0mrnMqmhUM+8R`1pOo36h|@#m0QwxBtUWT8o4czL${Sv!5{-ou%&H^Pji~r;Zkv6HaTgNvCJgq+W}zGS9dgC`fwFQUzf;zKU^K}Y`k{s zl6m*&j#eJ>yqMa8zm&|Y4^Wnm@D{{>Wx%J{5joKtx3CmxK&(#?G*;%cg z-*#n)<74}BuKE#Ug$2h2+y9bJ|J&4f6!8ghH#}@_uk=u1Xp@h7ru2$Ws{ypb-wHcPU$}!mgeOOr+!Kl#707AN# z7wZIDRbh+LGdutFI(C7@w|O#8$8io-KAfY;SvXKv_tgK zoZ!i#(sd6?Hl;IfOhz`^?BH{ojcdw5{I@sFLyCZE<0SX{jSNR%@Y^u?auPO#`cTp^-I2-vVB?U%e2ehOy5*n znsdd z#M?Ccc{;bV+0%0GA1@Az>iCU8C0k3NiZ%Bo&6)H?xx&!;SEd{n>OihxGo=m@8SLn>#DiFo_9JN zZz0teQ(wBBH(u`UqB-w5bB&-%Y(trM^N!I}L1E#EG`Jf@$1E?Oh{Z`zKudj-slJA$jX zE)TVzztW_1vMk4exrgs>ue%(T>wHIpDdLFL3y-%(&)U`WlBcb@`}(p*9BY&dL*JY^ zYp$3dT$`09Jm)FbiarLJpSi0pJ>$Am!7%ey^dSWX|GN3P@5?1- ~@!u+p!QG4d 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 D4~1bM literal 0 HcmV?d00001 diff --git a/public/icons/maskable-192.png b/public/icons/maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..36a114656ff95df2690f0514311d8a9a6ed17c91 GIT binary patch literal 1625 zcmcJQdpHw%7{`ChY;u`Pb(GPwT%tJQkcco%>{+R#2y5#&NwHC56lRTzrstGfr(COZ zgq&PM=CVk8%q=0qVlK^fE={h}j#E$1)1Up*^L*aR^Zxlh-}n2*9dmI~lGl(204QOd zF|O;C{eQ^8*0U==01f~Qg~g!V!qPBWW zD0{Y!m^I`WiN-hvXBn{O4#Bjqn)cilY?f`u%ORL3MaN5bzNon0TdL=<0d(p36m;ib7Hr*CvCCY)fKb6YE&eY&X9XD0t zM`kTf+_|-^pWrdul8Dj3BtZ&iz4YUZV*EA`+sg@RXvO2+(2}KXp^aMf>oZPjV`P;n z1bb*v4>@KSfQG3|5IM$3K^0WpJ~ldE7s-=Ax7hndKi!RdVh z2@?x#=_)YQ!Pd-4P^G-RF4L(S0*WDFw0HJ4;_=@=LG<+{Vb?ie&r$(@zffQgTf~Kp zcH;qkY80}6dCJ7{PT9e>XSoxB%Cr=oO!yN|+F?^%S_F-*^=(^xs6f;O@oO$m1#}k_ zaMt;b#Fa?8^zy}Gzxu_r>G`u+Ej$S7HuwfpFmRyqaocqDF85DtQkC?)mxo09r>l4E zF8?;W(!4y*2n8nMAixFakHJT1#UAdRdN4?4k#+rb4&tdjmGMgR|KvZhK1Wf|vo^z* z4%=;+5)b1R2GyC=5#FO@e#Bfg0V?n&1ahY}ntDiH;Uv*SRDYrPMbZb#Hr5{|K3<24 z7X&T71L;P*CirN86fQ2S=v=5rz#x`ok)<5!`?u%Sh5mLYv^-Ob^vP{~={{EBl7NSWX@%_0BcKXtD3_UAxd%PD#d_ zc2?zLpEPG^w)HhfH14!LUG$#|ul$8o2(_4~vj8 za&zyJQZ@B>RAu($HDm#|wc(|Mc}Wb@(~WzU%o3?XZVx9oHJ2mHLoWT|h6ae_ z+8yK>uac9}$SY1f3b8D^Z+8X4;}B@we9MT?zCwB&fJEK}wPGeplPu z%Vjq@{z~R9uDCJXucfHcb@cU_$xOl3uFI-vgNiu}pt*c5k0(I5MuPE=UUevNl$P#4 zWfs3TKMGg$*^md(Jnenai2l0|GFEp(kZ!JYMLk6OJ2ii=>>uoLN+cei7kbGY`{nfM z7C1y;iHi@MZoc6g`ygQzC`Nw-YaL^AVgKhZ{rl)k4T>ovwZon_SoB^gs z9C8xmULUxnMbx9lyu)gH%(ugX5If9|s`b>}lOyUea?YrYGwvZk>__dbIqDF5jh_WO aX$@wq;XYoQ#?D@U8360(f_dgZWc&wX|F~QL literal 0 HcmV?d00001 diff --git a/public/icons/maskable-512.png b/public/icons/maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..fa340d699dda30e99d1debf8cdab9bd0c62ca3ec GIT binary patch literal 4528 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 ZBBJ-$-==R9_9q68m{}coxX< { + e.waitUntil(caches.open(SURUM).then((c) => c.addAll(KABUK)).then(() => self.skipWaiting())); +}); +self.addEventListener('activate', (e) => { + e.waitUntil(caches.keys().then((ks) => Promise.all(ks.filter((k) => k !== SURUM).map((k) => caches.delete(k)))).then(() => self.clients.claim())); +}); +self.addEventListener('fetch', (e) => { + const url = new URL(e.request.url); + if (e.request.method !== 'GET' || url.origin !== self.location.origin) return; + // API: yalnız ağ — önbellekten eski ihale gösterme + if (url.pathname.startsWith('/api/')) return; + // Hash'li build varlıkları: cache-first (değişmez adlar) + if (url.pathname.startsWith('/assets/')) { + e.respondWith(caches.match(e.request).then((hit) => hit || fetch(e.request).then((r) => { + const kopya = r.clone(); caches.open(SURUM).then((c) => c.put(e.request, kopya)); return r; + }))); + return; + } + // Kabuk / sayfa: network-first, ağ yoksa önbellekten index + e.respondWith(fetch(e.request).then((r) => { + const kopya = r.clone(); caches.open(SURUM).then((c) => c.put(e.request, kopya)); return r; + }).catch(() => caches.match(e.request).then((hit) => hit || caches.match('/')))); +}); diff --git a/src/App.jsx b/src/App.jsx index a2dc49c..b8357dd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -23,6 +23,7 @@ import { ContractSearch } from './components/tools/ContractSearch'; import { VatCalculator } from './components/tools/VatCalculator'; import { SinirDeger } from './components/tools/SinirDeger'; import { KullanimKilavuzu } from './components/KullanimKilavuzu'; +import { MobilFiltreCekmecesi } from './components/MobilFiltreCekmecesi'; import { PriceAnalysis } from './components/tools/PriceAnalysis'; import { CompetitorRadar } from './components/tools/CompetitorRadar'; import { DocumentVault } from './components/tools/DocumentVault'; @@ -48,10 +49,13 @@ const MainContent = () => { {activeTab === 'list' && ( <> +
- + {/* Masaüstünde sabit panel; mobilde çekmece (yukarıda) */} +
- + {/* Sağ panel küçük ekranda listeyi uzatıyordu */} +
)} diff --git a/src/components/MobilFiltreCekmecesi.jsx b/src/components/MobilFiltreCekmecesi.jsx new file mode 100644 index 0000000..ca422be --- /dev/null +++ b/src/components/MobilFiltreCekmecesi.jsx @@ -0,0 +1,35 @@ +import React, { useState } from 'react'; +import { SlidersHorizontal, X } from 'lucide-react'; +import { LeftSidebar } from './LeftSidebar'; + +/** + * Mobil filtre çekmecesi: küçük ekranda sol menü listeyi aşağı itiyordu + * (72 satırlık panel). Artık lg altında gizli; "Filtreler" düğmesiyle + * soldan açılan çekmecede aynı bileşen çalışır. + */ +export const MobilFiltreCekmecesi = () => { + const [acik, setAcik] = useState(false); + return ( +
+ + {acik && ( +
setAcik(false)}> +
+
e.stopPropagation()} className="relative h-full w-[88%] max-w-sm bg-slate-100 shadow-2xl overflow-y-auto p-3 animate-fade-in"> +
+ Filtreler + +
+ +
+
+ )} +
+ ); +}; diff --git a/src/components/tools/PwaSettings.jsx b/src/components/tools/PwaSettings.jsx index 19a0074..701febe 100644 --- a/src/components/tools/PwaSettings.jsx +++ b/src/components/tools/PwaSettings.jsx @@ -57,9 +57,28 @@ export const PwaSettings = () => { }; const setQuiet = (patch) => setReminderSettings((s) => ({ ...s, ...patch })); - const handleInstallPwa = () => { - setInstalled(true); - confetti({ particleCount: 70 }); + // Gerçek PWA kurulumu: tarayıcı beforeinstallprompt verirse yerel diyalog; + // zaten standalone açıldıysa "yüklü"; iOS Safari'de olay yok → yol tarifi. + const [kurulumOlayi, setKurulumOlayi] = useState(null); + const [iosIpucu, setIosIpucu] = useState(false); + useEffect(() => { + const standalone = window.matchMedia?.('(display-mode: standalone)')?.matches || window.navigator.standalone === true; + if (standalone) setInstalled(true); + const yakala = (e) => { e.preventDefault(); setKurulumOlayi(e); }; + const kuruldu = () => { setInstalled(true); setKurulumOlayi(null); }; + window.addEventListener('beforeinstallprompt', yakala); + window.addEventListener('appinstalled', kuruldu); + return () => { window.removeEventListener('beforeinstallprompt', yakala); window.removeEventListener('appinstalled', kuruldu); }; + }, []); + const handleInstallPwa = async () => { + if (kurulumOlayi) { + kurulumOlayi.prompt(); + const { outcome } = await kurulumOlayi.userChoice.catch(() => ({ outcome: 'dismissed' })); + if (outcome === 'accepted') setInstalled(true); + setKurulumOlayi(null); + return; + } + setIosIpucu(true); // olay yoksa (iOS / zaten yüklü / desteklenmiyor) yol tarifi göster }; return ( @@ -237,7 +256,7 @@ export const PwaSettings = () => { {installed ? ( <> - Cihaza Yüklendi! + Uygulama olarak yüklü ) : ( <> @@ -246,6 +265,13 @@ export const PwaSettings = () => { )} + {iosIpucu && !installed && ( +
+

iPhone / iPad (Safari): alttaki Paylaş simgesi → Ana Ekrana Ekle.

+

Android (Chrome): sağ üst ⋮ menü → Uygulamayı yükle / Ana ekrana ekle.

+

Masaüstü (Chrome/Edge): adres çubuğunun sağındaki yükleme simgesi.

+
+ )}
diff --git a/src/main.jsx b/src/main.jsx index 54b39dd..76ac609 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -8,3 +8,8 @@ ReactDOM.createRoot(document.getElementById('root')).render( , ) + +// PWA: service worker yalnız üretimde (dev'de HMR ile çakışmasın) +if ('serviceWorker' in navigator && import.meta.env.PROD) { + window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js').catch(() => {}); }); +}