AdminLTE3

This commit is contained in:
2025-10-26 22:14:47 +09:00
parent 291fc63a4c
commit 9974811a3e
226 changed files with 88284 additions and 3406 deletions

View File

@@ -0,0 +1,203 @@
/* Enhanced Animations for SmartSolTech */
/* Gradient Animation */
@keyframes gradient-x {
0%, 100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
.animate-gradient-x {
background-size: 200% 200%;
animation: gradient-x 4s ease infinite;
}
/* Enhanced Blob Animation */
@keyframes blob {
0% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(30px, -50px) scale(1.1);
}
66% {
transform: translate(-20px, 20px) scale(0.9);
}
100% {
transform: translate(0px, 0px) scale(1);
}
}
.animate-blob {
animation: blob 7s infinite;
}
.animation-delay-2000 {
animation-delay: 2s;
}
.animation-delay-4000 {
animation-delay: 4s;
}
.animation-delay-6000 {
animation-delay: 6s;
}
/* Pulse Animation with Delay */
@keyframes pulse-delayed {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-pulse-delayed {
animation: pulse-delayed 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Floating Animation */
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
.animate-float {
animation: float 3s ease-in-out infinite;
}
/* Card Hover Effects */
.card-hover {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-hover:hover {
transform: translateY(-8px);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
/* Background Pattern */
.bg-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(59, 130, 246, 0.15) 1px, transparent 0);
background-size: 20px 20px;
}
/* Glowing Effect */
.glow {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.5);
}
.glow-purple {
box-shadow: 0 0 20px rgba(139, 92, 246, 0.5);
}
.glow-green {
box-shadow: 0 0 20px rgba(16, 185, 129, 0.5);
}
.glow-orange {
box-shadow: 0 0 20px rgba(251, 146, 60, 0.5);
}
/* Hero Section Specific */
.hero-section {
position: relative;
overflow: hidden;
}
.hero-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(1200px 600px at 10% -10%, rgba(99, 102, 241, 0.35), transparent 60%),
radial-gradient(1000px 500px at 110% 10%, rgba(168, 85, 247, 0.35), transparent 60%);
pointer-events: none;
z-index: 1;
}
/* Glass Effect */
.glass-effect {
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
/* Smooth Transitions */
.smooth-transition {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Text Shimmer Effect */
@keyframes shimmer {
0% {
background-position: -200% center;
}
100% {
background-position: 200% center;
}
}
.text-shimmer {
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.8), transparent);
background-size: 200% 100%;
animation: shimmer 2s infinite;
background-clip: text;
-webkit-background-clip: text;
}
/* Button Hover Effects */
.btn-enhanced {
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.btn-enhanced::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.btn-enhanced:hover::before {
left: 100%;
}
/* Responsive Adjustments */
@media (max-width: 768px) {
.animate-blob {
animation-duration: 10s;
}
.card-hover:hover {
transform: translateY(-4px);
}
}
/* Dark Mode Adjustments */
.dark .glass-effect {
background: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.dark .bg-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(59, 130, 246, 0.1) 1px, transparent 0);
}

View File

@@ -0,0 +1,203 @@
/* Enhanced Animations for SmartSolTech */
/* Gradient Animation */
@keyframes gradient-x {
0%, 100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
.animate-gradient-x {
background-size: 200% 200%;
animation: gradient-x 4s ease infinite;
}
/* Enhanced Blob Animation */
@keyframes blob {
0% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(30px, -50px) scale(1.1);
}
66% {
transform: translate(-20px, 20px) scale(0.9);
}
100% {
transform: translate(0px, 0px) scale(1);
}
}
.animate-blob {
animation: blob 7s infinite;
}
.animation-delay-2000 {
animation-delay: 2s;
}
.animation-delay-4000 {
animation-delay: 4s;
}
.animation-delay-6000 {
animation-delay: 6s;
}
/* Pulse Animation with Delay */
@keyframes pulse-delayed {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-pulse-delayed {
animation: pulse-delayed 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Floating Animation */
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
.animate-float {
animation: float 3s ease-in-out infinite;
}
/* Card Hover Effects */
.card-hover {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-hover:hover {
transform: translateY(-8px);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
/* Background Pattern */
.bg-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(59, 130, 246, 0.15) 1px, transparent 0);
background-size: 20px 20px;
}
/* Glowing Effect */
.glow {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.5);
}
.glow-purple {
box-shadow: 0 0 20px rgba(139, 92, 246, 0.5);
}
.glow-green {
box-shadow: 0 0 20px rgba(16, 185, 129, 0.5);
}
.glow-orange {
box-shadow: 0 0 20px rgba(251, 146, 60, 0.5);
}
/* Hero Section Specific */
.hero-section {
position: relative;
overflow: hidden;
}
.hero-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(1200px 600px at 10% -10%, rgba(99, 102, 241, 0.35), transparent 60%),
radial-gradient(1000px 500px at 110% 10%, rgba(168, 85, 247, 0.35), transparent 60%);
pointer-events: none;
z-index: 1;
}
/* Glass Effect */
.glass-effect {
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
/* Smooth Transitions */
.smooth-transition {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Text Shimmer Effect */
@keyframes shimmer {
0% {
background-position: -200% center;
}
100% {
background-position: 200% center;
}
}
.text-shimmer {
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.8), transparent);
background-size: 200% 100%;
animation: shimmer 2s infinite;
background-clip: text;
-webkit-background-clip: text;
}
/* Button Hover Effects */
.btn-enhanced {
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.btn-enhanced::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.btn-enhanced:hover::before {
left: 100%;
}
/* Responsive Adjustments */
@media (max-width: 768px) {
.animate-blob {
animation-duration: 10s;
}
.card-hover:hover {
transform: translateY(-4px);
}
}
/* Dark Mode Adjustments */
.dark .glass-effect {
background: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.dark .bg-pattern {
background-image: radial-gradient(circle at 1px 1px, rgba(59, 130, 246, 0.1) 1px, transparent 0);
}

View File

@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Logo design canvas -->
<rect x="60" y="50" width="80" height="80" rx="8" fill="rgba(255,255,255,0.9)" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
<!-- Logo elements - abstract brand symbol -->
<circle cx="100" cy="90" r="20" fill="none" stroke="rgba(59,130,246,0.6)" stroke-width="3"/>
<path d="M85 90 Q100 75 115 90 Q100 105 85 90" fill="rgba(139,92,246,0.5)"/>
<circle cx="100" cy="90" r="8" fill="rgba(245,158,11,0.7)"/>
<!-- Brand typography -->
<rect x="70" y="115" width="60" height="8" rx="4" fill="rgba(59,130,246,0.3)"/>
<!-- Color swatches -->
<rect x="20" y="30" width="15" height="15" rx="3" fill="rgba(59,130,246,0.8)"/>
<rect x="20" y="50" width="15" height="15" rx="3" fill="rgba(139,92,246,0.8)"/>
<rect x="20" y="70" width="15" height="15" rx="3" fill="rgba(245,158,11,0.8)"/>
<rect x="20" y="90" width="15" height="15" rx="3" fill="rgba(34,197,94,0.8)"/>
<rect x="20" y="110" width="15" height="15" rx="3" fill="rgba(239,68,68,0.8)"/>
<!-- Business cards -->
<rect x="150" y="40" width="30" height="20" rx="3" fill="rgba(255,255,255,0.8)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<rect x="152" y="45" width="8" height="3" rx="1.5" fill="rgba(59,130,246,0.5)"/>
<rect x="152" y="50" width="12" height="2" rx="1" fill="rgba(139,92,246,0.3)"/>
<rect x="152" y="54" width="10" height="2" rx="1" fill="rgba(245,158,11,0.3)"/>
<!-- Brand guidelines -->
<rect x="25" y="140" width="40" height="30" rx="4" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<rect x="30" y="150" width="30" height="3" rx="1.5" fill="rgba(59,130,246,0.4)"/>
<rect x="30" y="157" width="20" height="2" rx="1" fill="rgba(139,92,246,0.3)"/>
<rect x="30" y="162" width="25" height="2" rx="1" fill="rgba(245,158,11,0.3)"/>
<!-- Letterhead -->
<rect x="140" y="140" width="35" height="45" rx="4" fill="rgba(255,255,255,0.8)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<circle cx="157" cy="155" r="6" fill="rgba(59,130,246,0.3)" stroke="rgba(59,130,246,0.5)" stroke-width="1"/>
<rect x="145" y="165" width="20" height="2" rx="1" fill="rgba(139,92,246,0.3)"/>
<rect x="145" y="170" width="15" height="2" rx="1" fill="rgba(245,158,11,0.3)"/>
<rect x="145" y="175" width="18" height="2" rx="1" fill="rgba(34,197,94,0.3)"/>
<!-- Pencil/Design tool -->
<rect x="160" y="15" width="4" height="25" rx="2" fill="rgba(245,158,11,0.7)"/>
<path d="M160 40 L164 40 L162 45 Z" fill="rgba(139,92,246,0.6)"/>
<circle cx="162" cy="12" r="3" fill="rgba(236,72,153,0.6)"/>
<!-- Typography samples -->
<text x="75" y="180" fill="rgba(255,255,255,0.5)" font-family="serif" font-size="12" font-weight="bold">Aa</text>
<text x="95" y="180" fill="rgba(255,255,255,0.4)" font-family="sans-serif" font-size="10">Brand</text>
<text x="125" y="180" fill="rgba(255,255,255,0.3)" font-family="monospace" font-size="8">123</text>
<!-- Copyright symbol -->
<circle cx="175" cy="100" r="8" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<text x="171" y="105" fill="rgba(255,255,255,0.4)" font-family="serif" font-size="10" font-weight="bold">©</text>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Logo design canvas -->
<rect x="60" y="50" width="80" height="80" rx="8" fill="rgba(255,255,255,0.9)" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
<!-- Logo elements - abstract brand symbol -->
<circle cx="100" cy="90" r="20" fill="none" stroke="rgba(59,130,246,0.6)" stroke-width="3"/>
<path d="M85 90 Q100 75 115 90 Q100 105 85 90" fill="rgba(139,92,246,0.5)"/>
<circle cx="100" cy="90" r="8" fill="rgba(245,158,11,0.7)"/>
<!-- Brand typography -->
<rect x="70" y="115" width="60" height="8" rx="4" fill="rgba(59,130,246,0.3)"/>
<!-- Color swatches -->
<rect x="20" y="30" width="15" height="15" rx="3" fill="rgba(59,130,246,0.8)"/>
<rect x="20" y="50" width="15" height="15" rx="3" fill="rgba(139,92,246,0.8)"/>
<rect x="20" y="70" width="15" height="15" rx="3" fill="rgba(245,158,11,0.8)"/>
<rect x="20" y="90" width="15" height="15" rx="3" fill="rgba(34,197,94,0.8)"/>
<rect x="20" y="110" width="15" height="15" rx="3" fill="rgba(239,68,68,0.8)"/>
<!-- Business cards -->
<rect x="150" y="40" width="30" height="20" rx="3" fill="rgba(255,255,255,0.8)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<rect x="152" y="45" width="8" height="3" rx="1.5" fill="rgba(59,130,246,0.5)"/>
<rect x="152" y="50" width="12" height="2" rx="1" fill="rgba(139,92,246,0.3)"/>
<rect x="152" y="54" width="10" height="2" rx="1" fill="rgba(245,158,11,0.3)"/>
<!-- Brand guidelines -->
<rect x="25" y="140" width="40" height="30" rx="4" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<rect x="30" y="150" width="30" height="3" rx="1.5" fill="rgba(59,130,246,0.4)"/>
<rect x="30" y="157" width="20" height="2" rx="1" fill="rgba(139,92,246,0.3)"/>
<rect x="30" y="162" width="25" height="2" rx="1" fill="rgba(245,158,11,0.3)"/>
<!-- Letterhead -->
<rect x="140" y="140" width="35" height="45" rx="4" fill="rgba(255,255,255,0.8)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<circle cx="157" cy="155" r="6" fill="rgba(59,130,246,0.3)" stroke="rgba(59,130,246,0.5)" stroke-width="1"/>
<rect x="145" y="165" width="20" height="2" rx="1" fill="rgba(139,92,246,0.3)"/>
<rect x="145" y="170" width="15" height="2" rx="1" fill="rgba(245,158,11,0.3)"/>
<rect x="145" y="175" width="18" height="2" rx="1" fill="rgba(34,197,94,0.3)"/>
<!-- Pencil/Design tool -->
<rect x="160" y="15" width="4" height="25" rx="2" fill="rgba(245,158,11,0.7)"/>
<path d="M160 40 L164 40 L162 45 Z" fill="rgba(139,92,246,0.6)"/>
<circle cx="162" cy="12" r="3" fill="rgba(236,72,153,0.6)"/>
<!-- Typography samples -->
<text x="75" y="180" fill="rgba(255,255,255,0.5)" font-family="serif" font-size="12" font-weight="bold">Aa</text>
<text x="95" y="180" fill="rgba(255,255,255,0.4)" font-family="sans-serif" font-size="10">Brand</text>
<text x="125" y="180" fill="rgba(255,255,255,0.3)" font-family="monospace" font-size="8">123</text>
<!-- Copyright symbol -->
<circle cx="175" cy="100" r="8" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<text x="171" y="105" fill="rgba(255,255,255,0.4)" font-family="serif" font-size="10" font-weight="bold">©</text>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,69 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Light bulb -->
<circle cx="100" cy="85" r="25" fill="rgba(245,158,11,0.2)" stroke="rgba(245,158,11,0.6)" stroke-width="3"/>
<rect x="95" y="105" width="10" height="8" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="93" y="113" width="14" height="4" rx="2" fill="rgba(255,255,255,0.8)"/>
<!-- Light rays -->
<path d="M75 60 L80 65" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M125 60 L120 65" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M60 85 L65 85" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M140 85 L135 85" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M75 110 L80 105" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M125 110 L120 105" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<!-- Filament inside bulb -->
<path d="M90 75 Q100 70 110 75 Q100 80 90 75" stroke="rgba(245,158,11,0.8)" stroke-width="2" fill="none"/>
<path d="M90 85 Q100 90 110 85 Q100 95 90 85" stroke="rgba(245,158,11,0.8)" stroke-width="2" fill="none"/>
<!-- Gears -->
<circle cx="50" cy="140" r="12" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>
<circle cx="50" cy="140" r="8" fill="rgba(59,130,246,0.3)"/>
<rect x="46" y="132" width="8" height="3" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<rect x="46" y="145" width="8" height="3" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<rect x="42" y="138" width="3" height="8" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<rect x="55" y="138" width="3" height="8" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<circle cx="150" cy="50" r="10" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<circle cx="150" cy="50" r="6" fill="rgba(34,197,94,0.3)"/>
<rect x="147" y="43" width="6" height="2" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="147" y="55" width="6" height="2" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="144" y="48" width="2" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="154" y="48" width="2" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<!-- Charts/Analytics -->
<rect x="130" y="130" width="50" height="30" rx="4" fill="rgba(255,255,255,0.8)"/>
<rect x="135" y="145" width="6" height="12" fill="rgba(59,130,246,0.6)"/>
<rect x="143" y="140" width="6" height="17" fill="rgba(34,197,94,0.6)"/>
<rect x="151" y="148" width="6" height="9" fill="rgba(245,158,11,0.6)"/>
<rect x="159" y="142" width="6" height="15" fill="rgba(139,92,246,0.6)"/>
<rect x="167" y="146" width="6" height="11" fill="rgba(236,72,153,0.6)"/>
<!-- Puzzle pieces -->
<path d="M20 40 L35 40 Q40 35 45 40 L60 40 L60 55 Q55 60 60 65 L60 80 L45 80 Q40 75 35 80 L20 80 Z"
fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<circle cx="40" cy="60" r="3" fill="rgba(59,130,246,0.5)"/>
<!-- Arrow pointing up (growth) -->
<path d="M170 140 L170 120" stroke="rgba(34,197,94,0.8)" stroke-width="3"/>
<path d="M165 125 L170 120 L175 125" stroke="rgba(34,197,94,0.8)" stroke-width="3" fill="none"/>
<!-- Document/Report -->
<rect x="25" y="160" width="20" height="25" rx="2" fill="rgba(255,255,255,0.8)"/>
<rect x="28" y="165" width="14" height="2" rx="1" fill="rgba(59,130,246,0.4)"/>
<rect x="28" y="170" width="10" height="1.5" rx="0.75" fill="rgba(139,92,246,0.3)"/>
<rect x="28" y="174" width="12" height="1.5" rx="0.75" fill="rgba(245,158,11,0.3)"/>
<rect x="28" y="178" width="8" height="1.5" rx="0.75" fill="rgba(34,197,94,0.3)"/>
<!-- Clock/Time -->
<circle cx="170" cy="170" r="8" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<path d="M170 165 L170 170 L175 170" stroke="rgba(59,130,246,0.8)" stroke-width="2" fill="none"/>
<!-- Target/Goals -->
<circle cx="30" cy="30" r="8" fill="none" stroke="rgba(239,68,68,0.5)" stroke-width="2"/>
<circle cx="30" cy="30" r="5" fill="none" stroke="rgba(239,68,68,0.6)" stroke-width="1"/>
<circle cx="30" cy="30" r="2" fill="rgba(239,68,68,0.7)"/>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,69 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Light bulb -->
<circle cx="100" cy="85" r="25" fill="rgba(245,158,11,0.2)" stroke="rgba(245,158,11,0.6)" stroke-width="3"/>
<rect x="95" y="105" width="10" height="8" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="93" y="113" width="14" height="4" rx="2" fill="rgba(255,255,255,0.8)"/>
<!-- Light rays -->
<path d="M75 60 L80 65" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M125 60 L120 65" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M60 85 L65 85" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M140 85 L135 85" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M75 110 L80 105" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<path d="M125 110 L120 105" stroke="rgba(245,158,11,0.6)" stroke-width="2"/>
<!-- Filament inside bulb -->
<path d="M90 75 Q100 70 110 75 Q100 80 90 75" stroke="rgba(245,158,11,0.8)" stroke-width="2" fill="none"/>
<path d="M90 85 Q100 90 110 85 Q100 95 90 85" stroke="rgba(245,158,11,0.8)" stroke-width="2" fill="none"/>
<!-- Gears -->
<circle cx="50" cy="140" r="12" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>
<circle cx="50" cy="140" r="8" fill="rgba(59,130,246,0.3)"/>
<rect x="46" y="132" width="8" height="3" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<rect x="46" y="145" width="8" height="3" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<rect x="42" y="138" width="3" height="8" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<rect x="55" y="138" width="3" height="8" rx="1.5" fill="rgba(255,255,255,0.6)"/>
<circle cx="150" cy="50" r="10" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<circle cx="150" cy="50" r="6" fill="rgba(34,197,94,0.3)"/>
<rect x="147" y="43" width="6" height="2" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="147" y="55" width="6" height="2" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="144" y="48" width="2" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="154" y="48" width="2" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<!-- Charts/Analytics -->
<rect x="130" y="130" width="50" height="30" rx="4" fill="rgba(255,255,255,0.8)"/>
<rect x="135" y="145" width="6" height="12" fill="rgba(59,130,246,0.6)"/>
<rect x="143" y="140" width="6" height="17" fill="rgba(34,197,94,0.6)"/>
<rect x="151" y="148" width="6" height="9" fill="rgba(245,158,11,0.6)"/>
<rect x="159" y="142" width="6" height="15" fill="rgba(139,92,246,0.6)"/>
<rect x="167" y="146" width="6" height="11" fill="rgba(236,72,153,0.6)"/>
<!-- Puzzle pieces -->
<path d="M20 40 L35 40 Q40 35 45 40 L60 40 L60 55 Q55 60 60 65 L60 80 L45 80 Q40 75 35 80 L20 80 Z"
fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<circle cx="40" cy="60" r="3" fill="rgba(59,130,246,0.5)"/>
<!-- Arrow pointing up (growth) -->
<path d="M170 140 L170 120" stroke="rgba(34,197,94,0.8)" stroke-width="3"/>
<path d="M165 125 L170 120 L175 125" stroke="rgba(34,197,94,0.8)" stroke-width="3" fill="none"/>
<!-- Document/Report -->
<rect x="25" y="160" width="20" height="25" rx="2" fill="rgba(255,255,255,0.8)"/>
<rect x="28" y="165" width="14" height="2" rx="1" fill="rgba(59,130,246,0.4)"/>
<rect x="28" y="170" width="10" height="1.5" rx="0.75" fill="rgba(139,92,246,0.3)"/>
<rect x="28" y="174" width="12" height="1.5" rx="0.75" fill="rgba(245,158,11,0.3)"/>
<rect x="28" y="178" width="8" height="1.5" rx="0.75" fill="rgba(34,197,94,0.3)"/>
<!-- Clock/Time -->
<circle cx="170" cy="170" r="8" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<path d="M170 165 L170 170 L175 170" stroke="rgba(59,130,246,0.8)" stroke-width="2" fill="none"/>
<!-- Target/Goals -->
<circle cx="30" cy="30" r="8" fill="none" stroke="rgba(239,68,68,0.5)" stroke-width="2"/>
<circle cx="30" cy="30" r="5" fill="none" stroke="rgba(239,68,68,0.6)" stroke-width="1"/>
<circle cx="30" cy="30" r="2" fill="rgba(239,68,68,0.7)"/>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,52 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Main gear -->
<circle cx="100" cy="100" r="30" fill="none" stroke="rgba(255,255,255,0.6)" stroke-width="4"/>
<circle cx="100" cy="100" r="20" fill="rgba(59,130,246,0.3)"/>
<!-- Gear teeth -->
<rect x="95" y="65" width="10" height="8" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="95" y="127" width="10" height="8" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="65" y="95" width="8" height="10" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="127" y="95" width="8" height="10" rx="2" fill="rgba(255,255,255,0.7)"/>
<!-- Diagonal gear teeth -->
<rect x="80" y="75" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(45 84 79)"/>
<rect x="112" y="75" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(-45 116 79)"/>
<rect x="80" y="117" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(-45 84 121)"/>
<rect x="112" y="117" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(45 116 121)"/>
<!-- Center circle -->
<circle cx="100" cy="100" r="8" fill="rgba(255,255,255,0.8)"/>
<!-- Smaller gears -->
<circle cx="60" cy="60" r="15" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<circle cx="60" cy="60" r="10" fill="rgba(34,197,94,0.3)"/>
<rect x="57" y="48" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="57" y="68" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="48" y="57" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="68" y="57" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<circle cx="140" cy="140" r="15" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<circle cx="140" cy="140" r="10" fill="rgba(245,158,11,0.3)"/>
<rect x="137" y="128" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="137" y="148" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="128" y="137" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="148" y="137" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<!-- Connecting lines -->
<path d="M70 70 L90 90" stroke="rgba(255,255,255,0.3)" stroke-width="2" stroke-dasharray="4,4"/>
<path d="M130 130 L110 110" stroke="rgba(255,255,255,0.3)" stroke-width="2" stroke-dasharray="4,4"/>
<!-- Decorative elements -->
<circle cx="30" cy="170" r="4" fill="rgba(255,255,255,0.2)"/>
<circle cx="170" cy="30" r="4" fill="rgba(255,255,255,0.2)"/>
<!-- Tool icons -->
<rect x="20" y="25" width="15" height="3" rx="1.5" fill="rgba(255,255,255,0.4)"/>
<circle cx="22" cy="26.5" r="1.5" fill="rgba(255,255,255,0.6)"/>
<path d="M165 165 L175 175 M170 160 L180 170" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,52 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Main gear -->
<circle cx="100" cy="100" r="30" fill="none" stroke="rgba(255,255,255,0.6)" stroke-width="4"/>
<circle cx="100" cy="100" r="20" fill="rgba(59,130,246,0.3)"/>
<!-- Gear teeth -->
<rect x="95" y="65" width="10" height="8" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="95" y="127" width="10" height="8" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="65" y="95" width="8" height="10" rx="2" fill="rgba(255,255,255,0.7)"/>
<rect x="127" y="95" width="8" height="10" rx="2" fill="rgba(255,255,255,0.7)"/>
<!-- Diagonal gear teeth -->
<rect x="80" y="75" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(45 84 79)"/>
<rect x="112" y="75" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(-45 116 79)"/>
<rect x="80" y="117" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(-45 84 121)"/>
<rect x="112" y="117" width="8" height="8" rx="2" fill="rgba(255,255,255,0.6)" transform="rotate(45 116 121)"/>
<!-- Center circle -->
<circle cx="100" cy="100" r="8" fill="rgba(255,255,255,0.8)"/>
<!-- Smaller gears -->
<circle cx="60" cy="60" r="15" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<circle cx="60" cy="60" r="10" fill="rgba(34,197,94,0.3)"/>
<rect x="57" y="48" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="57" y="68" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="48" y="57" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="68" y="57" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<circle cx="140" cy="140" r="15" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
<circle cx="140" cy="140" r="10" fill="rgba(245,158,11,0.3)"/>
<rect x="137" y="128" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="137" y="148" width="6" height="4" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="128" y="137" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<rect x="148" y="137" width="4" height="6" rx="1" fill="rgba(255,255,255,0.5)"/>
<!-- Connecting lines -->
<path d="M70 70 L90 90" stroke="rgba(255,255,255,0.3)" stroke-width="2" stroke-dasharray="4,4"/>
<path d="M130 130 L110 110" stroke="rgba(255,255,255,0.3)" stroke-width="2" stroke-dasharray="4,4"/>
<!-- Decorative elements -->
<circle cx="30" cy="170" r="4" fill="rgba(255,255,255,0.2)"/>
<circle cx="170" cy="30" r="4" fill="rgba(255,255,255,0.2)"/>
<!-- Tool icons -->
<rect x="20" y="25" width="15" height="3" rx="1.5" fill="rgba(255,255,255,0.4)"/>
<circle cx="22" cy="26.5" r="1.5" fill="rgba(255,255,255,0.6)"/>
<path d="M165 165 L175 175 M170 160 L180 170" stroke="rgba(255,255,255,0.4)" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,56 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Chart/Graph -->
<rect x="40" y="60" width="120" height="80" rx="8" fill="rgba(255,255,255,0.9)"/>
<!-- Chart bars -->
<rect x="55" y="100" width="12" height="30" fill="rgba(34,197,94,0.7)"/>
<rect x="72" y="85" width="12" height="45" fill="rgba(59,130,246,0.7)"/>
<rect x="89" y="95" width="12" height="35" fill="rgba(245,158,11,0.7)"/>
<rect x="106" y="75" width="12" height="55" fill="rgba(239,68,68,0.7)"/>
<rect x="123" y="90" width="12" height="40" fill="rgba(139,92,246,0.7)"/>
<!-- Trend line -->
<path d="M55 110 L72 95 L89 105 L106 85 L123 100" stroke="rgba(236,72,153,0.8)" stroke-width="3" fill="none"/>
<!-- Chart dots -->
<circle cx="61" cy="110" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="78" cy="95" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="95" cy="105" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="112" cy="85" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="129" cy="100" r="3" fill="rgba(236,72,153,0.8)"/>
<!-- Megaphone/Speaker -->
<path d="M20 120 L35 115 L35 125 Z" fill="rgba(255,255,255,0.8)"/>
<rect x="35" y="117" width="15" height="6" rx="3" fill="rgba(255,255,255,0.8)"/>
<path d="M50 115 Q60 115 60 120 Q60 125 50 125" stroke="rgba(255,255,255,0.6)" stroke-width="2" fill="none"/>
<!-- Social media icons -->
<circle cx="170" cy="40" r="8" fill="rgba(59,130,246,0.7)"/>
<text x="166" y="45" fill="white" font-family="sans-serif" font-size="8" font-weight="bold">f</text>
<circle cx="170" cy="60" r="8" fill="rgba(29,161,242,0.7)"/>
<text x="167" y="65" fill="white" font-family="sans-serif" font-size="8" font-weight="bold">t</text>
<circle cx="170" cy="80" r="8" fill="rgba(225,48,108,0.7)"/>
<text x="167" y="85" fill="white" font-family="sans-serif" font-size="8" font-weight="bold">i</text>
<!-- Target/Bullseye -->
<circle cx="30" cy="170" r="12" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>
<circle cx="30" cy="170" r="8" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>
<circle cx="30" cy="170" r="4" fill="rgba(239,68,68,0.7)"/>
<!-- SEO elements -->
<text x="140" y="170" fill="rgba(255,255,255,0.5)" font-family="sans-serif" font-size="12" font-weight="bold">SEO</text>
<!-- Growth arrow -->
<path d="M140 110 L155 95" stroke="rgba(34,197,94,0.8)" stroke-width="3" fill="none"/>
<path d="M150 95 L155 95 L155 100" stroke="rgba(34,197,94,0.8)" stroke-width="3" fill="none"/>
<!-- Analytics symbols -->
<circle cx="60" cy="40" r="3" fill="rgba(255,255,255,0.4)"/>
<circle cx="75" cy="35" r="4" fill="rgba(255,255,255,0.3)"/>
<circle cx="90" cy="45" r="2" fill="rgba(255,255,255,0.4)"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,56 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Chart/Graph -->
<rect x="40" y="60" width="120" height="80" rx="8" fill="rgba(255,255,255,0.9)"/>
<!-- Chart bars -->
<rect x="55" y="100" width="12" height="30" fill="rgba(34,197,94,0.7)"/>
<rect x="72" y="85" width="12" height="45" fill="rgba(59,130,246,0.7)"/>
<rect x="89" y="95" width="12" height="35" fill="rgba(245,158,11,0.7)"/>
<rect x="106" y="75" width="12" height="55" fill="rgba(239,68,68,0.7)"/>
<rect x="123" y="90" width="12" height="40" fill="rgba(139,92,246,0.7)"/>
<!-- Trend line -->
<path d="M55 110 L72 95 L89 105 L106 85 L123 100" stroke="rgba(236,72,153,0.8)" stroke-width="3" fill="none"/>
<!-- Chart dots -->
<circle cx="61" cy="110" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="78" cy="95" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="95" cy="105" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="112" cy="85" r="3" fill="rgba(236,72,153,0.8)"/>
<circle cx="129" cy="100" r="3" fill="rgba(236,72,153,0.8)"/>
<!-- Megaphone/Speaker -->
<path d="M20 120 L35 115 L35 125 Z" fill="rgba(255,255,255,0.8)"/>
<rect x="35" y="117" width="15" height="6" rx="3" fill="rgba(255,255,255,0.8)"/>
<path d="M50 115 Q60 115 60 120 Q60 125 50 125" stroke="rgba(255,255,255,0.6)" stroke-width="2" fill="none"/>
<!-- Social media icons -->
<circle cx="170" cy="40" r="8" fill="rgba(59,130,246,0.7)"/>
<text x="166" y="45" fill="white" font-family="sans-serif" font-size="8" font-weight="bold">f</text>
<circle cx="170" cy="60" r="8" fill="rgba(29,161,242,0.7)"/>
<text x="167" y="65" fill="white" font-family="sans-serif" font-size="8" font-weight="bold">t</text>
<circle cx="170" cy="80" r="8" fill="rgba(225,48,108,0.7)"/>
<text x="167" y="85" fill="white" font-family="sans-serif" font-size="8" font-weight="bold">i</text>
<!-- Target/Bullseye -->
<circle cx="30" cy="170" r="12" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>
<circle cx="30" cy="170" r="8" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="2"/>
<circle cx="30" cy="170" r="4" fill="rgba(239,68,68,0.7)"/>
<!-- SEO elements -->
<text x="140" y="170" fill="rgba(255,255,255,0.5)" font-family="sans-serif" font-size="12" font-weight="bold">SEO</text>
<!-- Growth arrow -->
<path d="M140 110 L155 95" stroke="rgba(34,197,94,0.8)" stroke-width="3" fill="none"/>
<path d="M150 95 L155 95 L155 100" stroke="rgba(34,197,94,0.8)" stroke-width="3" fill="none"/>
<!-- Analytics symbols -->
<circle cx="60" cy="40" r="3" fill="rgba(255,255,255,0.4)"/>
<circle cx="75" cy="35" r="4" fill="rgba(255,255,255,0.3)"/>
<circle cx="90" cy="45" r="2" fill="rgba(255,255,255,0.4)"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,43 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Main phone -->
<rect x="70" y="40" width="60" height="120" rx="12" fill="rgba(255,255,255,0.95)" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
<!-- Screen -->
<rect x="75" y="55" width="50" height="90" rx="6" fill="rgba(34,197,94,0.2)"/>
<!-- Status bar -->
<rect x="78" y="58" width="44" height="8" fill="rgba(34,197,94,0.4)"/>
<!-- App icons grid -->
<rect x="80" y="75" width="12" height="12" rx="3" fill="rgba(59,130,246,0.7)"/>
<rect x="95" y="75" width="12" height="12" rx="3" fill="rgba(239,68,68,0.7)"/>
<rect x="110" y="75" width="12" height="12" rx="3" fill="rgba(245,158,11,0.7)"/>
<rect x="80" y="92" width="12" height="12" rx="3" fill="rgba(139,92,246,0.7)"/>
<rect x="95" y="92" width="12" height="12" rx="3" fill="rgba(16,185,129,0.7)"/>
<rect x="110" y="92" width="12" height="12" rx="3" fill="rgba(236,72,153,0.7)"/>
<!-- Navigation bar -->
<circle cx="100" cy="130" r="8" fill="rgba(255,255,255,0.8)" stroke="rgba(34,197,94,0.3)" stroke-width="2"/>
<!-- Home button -->
<circle cx="100" cy="150" r="4" fill="rgba(255,255,255,0.6)"/>
<!-- Secondary phones -->
<rect x="35" y="80" width="25" height="45" rx="6" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<rect x="38" y="88" width="19" height="25" fill="rgba(34,197,94,0.3)"/>
<rect x="140" y="90" width="25" height="45" rx="6" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<rect x="143" y="98" width="19" height="25" fill="rgba(34,197,94,0.3)"/>
<!-- Decorative elements -->
<circle cx="50" cy="50" r="4" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<circle cx="150" cy="160" r="6" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- WiFi and signal indicators -->
<path d="M25 25 L35 25 M30 20 L30 30 M27 23 L33 23" stroke="rgba(255,255,255,0.3)" stroke-width="2" fill="none"/>
<path d="M165 25 Q175 25 175 35 Q175 45 165 45" stroke="rgba(255,255,255,0.3)" stroke-width="2" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,43 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Main phone -->
<rect x="70" y="40" width="60" height="120" rx="12" fill="rgba(255,255,255,0.95)" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
<!-- Screen -->
<rect x="75" y="55" width="50" height="90" rx="6" fill="rgba(34,197,94,0.2)"/>
<!-- Status bar -->
<rect x="78" y="58" width="44" height="8" fill="rgba(34,197,94,0.4)"/>
<!-- App icons grid -->
<rect x="80" y="75" width="12" height="12" rx="3" fill="rgba(59,130,246,0.7)"/>
<rect x="95" y="75" width="12" height="12" rx="3" fill="rgba(239,68,68,0.7)"/>
<rect x="110" y="75" width="12" height="12" rx="3" fill="rgba(245,158,11,0.7)"/>
<rect x="80" y="92" width="12" height="12" rx="3" fill="rgba(139,92,246,0.7)"/>
<rect x="95" y="92" width="12" height="12" rx="3" fill="rgba(16,185,129,0.7)"/>
<rect x="110" y="92" width="12" height="12" rx="3" fill="rgba(236,72,153,0.7)"/>
<!-- Navigation bar -->
<circle cx="100" cy="130" r="8" fill="rgba(255,255,255,0.8)" stroke="rgba(34,197,94,0.3)" stroke-width="2"/>
<!-- Home button -->
<circle cx="100" cy="150" r="4" fill="rgba(255,255,255,0.6)"/>
<!-- Secondary phones -->
<rect x="35" y="80" width="25" height="45" rx="6" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<rect x="38" y="88" width="19" height="25" fill="rgba(34,197,94,0.3)"/>
<rect x="140" y="90" width="25" height="45" rx="6" fill="rgba(255,255,255,0.7)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<rect x="143" y="98" width="19" height="25" fill="rgba(34,197,94,0.3)"/>
<!-- Decorative elements -->
<circle cx="50" cy="50" r="4" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<circle cx="150" cy="160" r="6" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- WiFi and signal indicators -->
<path d="M25 25 L35 25 M30 20 L30 30 M27 23 L33 23" stroke="rgba(255,255,255,0.3)" stroke-width="2" fill="none"/>
<path d="M165 25 Q175 25 175 35 Q175 45 165 45" stroke="rgba(255,255,255,0.3)" stroke-width="2" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,47 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Design canvas/artboard -->
<rect x="50" y="50" width="100" height="80" rx="8" fill="rgba(255,255,255,0.9)" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
<!-- Design elements - wireframe -->
<rect x="60" y="65" width="80" height="8" rx="4" fill="rgba(139,92,246,0.3)"/>
<rect x="60" y="80" width="50" height="6" rx="3" fill="rgba(236,72,153,0.4)"/>
<rect x="60" y="92" width="70" height="6" rx="3" fill="rgba(59,130,246,0.4)"/>
<!-- Button mockups -->
<rect x="60" y="105" width="25" height="12" rx="6" fill="rgba(34,197,94,0.6)"/>
<rect x="90" y="105" width="25" height="12" rx="6" fill="rgba(239,68,68,0.3)" stroke="rgba(239,68,68,0.5)" stroke-width="1"/>
<!-- Color palette -->
<circle cx="170" cy="60" r="8" fill="rgba(59,130,246,0.8)"/>
<circle cx="170" cy="80" r="8" fill="rgba(34,197,94,0.8)"/>
<circle cx="170" cy="100" r="8" fill="rgba(245,158,11,0.8)"/>
<circle cx="170" cy="120" r="8" fill="rgba(236,72,153,0.8)"/>
<!-- Design tools -->
<rect x="20" y="30" width="40" height="6" rx="3" fill="rgba(255,255,255,0.7)"/>
<circle cx="25" cy="33" r="2" fill="rgba(59,130,246,0.7)"/>
<circle cx="32" cy="33" r="2" fill="rgba(34,197,94,0.7)"/>
<circle cx="39" cy="33" r="2" fill="rgba(239,68,68,0.7)"/>
<!-- Cursor/pointer -->
<path d="M25 160 L35 170 L30 175 L20 170 Z" fill="rgba(255,255,255,0.8)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<!-- Grid lines -->
<defs>
<pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
</pattern>
</defs>
<rect x="50" y="50" width="100" height="80" fill="url(#grid)"/>
<!-- Floating design elements -->
<rect x="160" y="140" width="20" height="12" rx="6" fill="rgba(255,255,255,0.5)" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<circle cx="30" cy="140" r="6" fill="rgba(255,255,255,0.3)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- Typography indicator -->
<text x="25" y="180" fill="rgba(255,255,255,0.4)" font-family="serif" font-size="16" font-weight="bold">Aa</text>
<text x="150" y="180" fill="rgba(255,255,255,0.4)" font-family="sans-serif" font-size="12">UI/UX</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,47 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Design canvas/artboard -->
<rect x="50" y="50" width="100" height="80" rx="8" fill="rgba(255,255,255,0.9)" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
<!-- Design elements - wireframe -->
<rect x="60" y="65" width="80" height="8" rx="4" fill="rgba(139,92,246,0.3)"/>
<rect x="60" y="80" width="50" height="6" rx="3" fill="rgba(236,72,153,0.4)"/>
<rect x="60" y="92" width="70" height="6" rx="3" fill="rgba(59,130,246,0.4)"/>
<!-- Button mockups -->
<rect x="60" y="105" width="25" height="12" rx="6" fill="rgba(34,197,94,0.6)"/>
<rect x="90" y="105" width="25" height="12" rx="6" fill="rgba(239,68,68,0.3)" stroke="rgba(239,68,68,0.5)" stroke-width="1"/>
<!-- Color palette -->
<circle cx="170" cy="60" r="8" fill="rgba(59,130,246,0.8)"/>
<circle cx="170" cy="80" r="8" fill="rgba(34,197,94,0.8)"/>
<circle cx="170" cy="100" r="8" fill="rgba(245,158,11,0.8)"/>
<circle cx="170" cy="120" r="8" fill="rgba(236,72,153,0.8)"/>
<!-- Design tools -->
<rect x="20" y="30" width="40" height="6" rx="3" fill="rgba(255,255,255,0.7)"/>
<circle cx="25" cy="33" r="2" fill="rgba(59,130,246,0.7)"/>
<circle cx="32" cy="33" r="2" fill="rgba(34,197,94,0.7)"/>
<circle cx="39" cy="33" r="2" fill="rgba(239,68,68,0.7)"/>
<!-- Cursor/pointer -->
<path d="M25 160 L35 170 L30 175 L20 170 Z" fill="rgba(255,255,255,0.8)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
<!-- Grid lines -->
<defs>
<pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="0.5"/>
</pattern>
</defs>
<rect x="50" y="50" width="100" height="80" fill="url(#grid)"/>
<!-- Floating design elements -->
<rect x="160" y="140" width="20" height="12" rx="6" fill="rgba(255,255,255,0.5)" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<circle cx="30" cy="140" r="6" fill="rgba(255,255,255,0.3)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- Typography indicator -->
<text x="25" y="180" fill="rgba(255,255,255,0.4)" font-family="serif" font-size="16" font-weight="bold">Aa</text>
<text x="150" y="180" fill="rgba(255,255,255,0.4)" font-family="sans-serif" font-size="12">UI/UX</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Browser window -->
<rect x="40" y="60" width="120" height="80" rx="8" fill="rgba(255,255,255,0.9)"/>
<!-- Browser header -->
<rect x="40" y="60" width="120" height="20" rx="8" fill="rgba(59,130,246,0.8)"/>
<!-- Browser dots -->
<circle cx="50" cy="70" r="3" fill="rgba(239,68,68,0.8)"/>
<circle cx="60" cy="70" r="3" fill="rgba(245,158,11,0.8)"/>
<circle cx="70" cy="70" r="3" fill="rgba(34,197,94,0.8)"/>
<!-- Code lines -->
<rect x="50" y="90" width="60" height="3" rx="1.5" fill="rgba(59,130,246,0.6)"/>
<rect x="50" y="100" width="40" height="3" rx="1.5" fill="rgba(16,185,129,0.6)"/>
<rect x="50" y="110" width="80" height="3" rx="1.5" fill="rgba(139,92,246,0.6)"/>
<rect x="50" y="120" width="30" height="3" rx="1.5" fill="rgba(245,158,11,0.6)"/>
<!-- Mobile phone -->
<rect x="130" y="110" width="25" height="40" rx="4" fill="rgba(255,255,255,0.9)" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<rect x="132" y="115" width="21" height="25" fill="rgba(59,130,246,0.3)"/>
<circle cx="142.5" cy="145" r="2" fill="rgba(255,255,255,0.7)"/>
<!-- Decorative elements -->
<circle cx="170" cy="80" r="8" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<circle cx="30" cy="120" r="6" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- Code brackets -->
<text x="20" y="50" fill="rgba(255,255,255,0.4)" font-family="monospace" font-size="20" font-weight="bold">&lt;/&gt;</text>
<text x="160" y="170" fill="rgba(255,255,255,0.4)" font-family="monospace" font-size="16" font-weight="bold">{}</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Background circle -->
<circle cx="100" cy="100" r="90" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="2"/>
<!-- Browser window -->
<rect x="40" y="60" width="120" height="80" rx="8" fill="rgba(255,255,255,0.9)"/>
<!-- Browser header -->
<rect x="40" y="60" width="120" height="20" rx="8" fill="rgba(59,130,246,0.8)"/>
<!-- Browser dots -->
<circle cx="50" cy="70" r="3" fill="rgba(239,68,68,0.8)"/>
<circle cx="60" cy="70" r="3" fill="rgba(245,158,11,0.8)"/>
<circle cx="70" cy="70" r="3" fill="rgba(34,197,94,0.8)"/>
<!-- Code lines -->
<rect x="50" y="90" width="60" height="3" rx="1.5" fill="rgba(59,130,246,0.6)"/>
<rect x="50" y="100" width="40" height="3" rx="1.5" fill="rgba(16,185,129,0.6)"/>
<rect x="50" y="110" width="80" height="3" rx="1.5" fill="rgba(139,92,246,0.6)"/>
<rect x="50" y="120" width="30" height="3" rx="1.5" fill="rgba(245,158,11,0.6)"/>
<!-- Mobile phone -->
<rect x="130" y="110" width="25" height="40" rx="4" fill="rgba(255,255,255,0.9)" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<rect x="132" y="115" width="21" height="25" fill="rgba(59,130,246,0.3)"/>
<circle cx="142.5" cy="145" r="2" fill="rgba(255,255,255,0.7)"/>
<!-- Decorative elements -->
<circle cx="170" cy="80" r="8" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<circle cx="30" cy="120" r="6" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- Code brackets -->
<text x="20" y="50" fill="rgba(255,255,255,0.4)" font-family="monospace" font-size="20" font-weight="bold">&lt;/&gt;</text>
<text x="160" y="170" fill="rgba(255,255,255,0.4)" font-family="monospace" font-size="16" font-weight="bold">{}</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,414 @@
// Service Worker for SmartSolTech PWA
const CACHE_NAME = 'smartsoltech-v1.0.2';
const STATIC_CACHE_NAME = 'smartsoltech-static-v1.0.2';
const DYNAMIC_CACHE_NAME = 'smartsoltech-dynamic-v1.0.2';
// Files to cache immediately
const STATIC_FILES = [
'/',
'/css/main.css',
'/css/fixes.css',
'/css/dark-theme.css',
'/js/main.js',
'/vendor/jquery/jquery-3.6.0.min.js',
'/vendor/bootstrap/bootstrap.bundle.min.js',
'/vendor/bootstrap/bootstrap.min.css',
'/vendor/adminlte/adminlte.min.js',
'/vendor/adminlte/adminlte.min.css',
'/vendor/adminlte/fontawesome.min.css',
'/images/logo.png',
'/images/icon-192x192.png',
'/images/icon-144x144.png',
'/manifest.json',
'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap',
'https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap',
'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css',
'https://unpkg.com/aos@2.3.1/dist/aos.css',
'https://unpkg.com/aos@2.3.1/dist/aos.js'
];
// Routes to cache dynamically
const DYNAMIC_ROUTES = [
'/about',
'/services',
'/portfolio',
'/calculator',
'/contact'
];
// API endpoints to cache
const API_CACHE_PATTERNS = [
/^\/api\/portfolio/,
/^\/api\/services/,
/^\/api\/calculator\/services/
];
// Install event - cache static files
self.addEventListener('install', event => {
console.log('Service Worker: Installing...');
event.waitUntil(
caches.open(STATIC_CACHE_NAME)
.then(cache => {
console.log('Service Worker: Caching static files');
return cache.addAll(STATIC_FILES);
})
.then(() => {
console.log('Service Worker: Static files cached');
return self.skipWaiting();
})
.catch(error => {
console.error('Service Worker: Error caching static files', error);
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', event => {
console.log('Service Worker: Activating...');
event.waitUntil(
caches.keys()
.then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== STATIC_CACHE_NAME &&
cacheName !== DYNAMIC_CACHE_NAME) {
console.log('Service Worker: Deleting old cache', cacheName);
return caches.delete(cacheName);
}
})
);
})
.then(() => {
console.log('Service Worker: Activated');
return self.clients.claim();
})
);
});
// Fetch event - serve cached files or fetch from network
self.addEventListener('fetch', event => {
const request = event.request;
const url = new URL(request.url);
// Skip non-GET requests
if (request.method !== 'GET') {
return;
}
// Skip Chrome extension requests
if (url.protocol === 'chrome-extension:') {
return;
}
// Handle different types of requests
if (isStaticFile(request.url)) {
event.respondWith(cacheFirst(request));
} else if (isAPIRequest(request.url)) {
event.respondWith(networkFirst(request));
} else if (isDynamicRoute(request.url) || url.pathname === '/') {
// For main pages, always check network first to ensure language consistency
event.respondWith(networkFirst(request));
} else {
event.respondWith(networkFirst(request));
}
});
// Cache strategies
async function cacheFirst(request) {
try {
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
const networkResponse = await fetch(request);
const cache = await caches.open(STATIC_CACHE_NAME);
cache.put(request, networkResponse.clone());
return networkResponse;
} catch (error) {
console.error('Cache first strategy failed:', error);
return new Response('Offline', { status: 503 });
}
}
async function networkFirst(request) {
try {
const networkResponse = await fetch(request);
// Cache successful responses
if (networkResponse.ok) {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
console.log('Network first: Falling back to cache for', request.url);
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
// Return offline page for navigation requests
if (request.mode === 'navigate') {
return caches.match('/offline.html') || new Response('Offline', {
status: 503,
headers: { 'Content-Type': 'text/html' }
});
}
return new Response('Network Error', { status: 503 });
}
}
async function staleWhileRevalidate(request) {
try {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
const cachedResponse = await cache.match(request);
const fetchPromise = fetch(request).then(networkResponse => {
if (networkResponse && networkResponse.ok) {
cache.put(request, networkResponse.clone());
}
return networkResponse;
}).catch(error => {
console.log('staleWhileRevalidate fetch failed:', error);
return null;
});
return cachedResponse || fetchPromise || new Response('Not available', { status: 503 });
} catch (error) {
console.error('staleWhileRevalidate error:', error);
return new Response('Service unavailable', { status: 503 });
}
}
// Helper functions
function isStaticFile(url) {
return url.includes('/css/') ||
url.includes('/js/') ||
url.includes('/images/') ||
url.includes('/fonts/') ||
url.includes('googleapis.com') ||
url.includes('cdnjs.cloudflare.com');
}
function isAPIRequest(url) {
return url.includes('/api/') ||
API_CACHE_PATTERNS.some(pattern => pattern.test(url));
}
function isDynamicRoute(url) {
const pathname = new URL(url).pathname;
return DYNAMIC_ROUTES.includes(pathname) ||
pathname.startsWith('/portfolio/') ||
pathname.startsWith('/services/');
}
// Background sync for form submissions
self.addEventListener('sync', event => {
console.log('Service Worker: Background sync triggered', event.tag);
if (event.tag === 'contact-form-sync') {
event.waitUntil(syncContactForms());
}
});
async function syncContactForms() {
try {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
const requests = await cache.keys();
const contactRequests = requests.filter(request =>
request.url.includes('/api/contact/submit')
);
for (const request of contactRequests) {
try {
await fetch(request);
await cache.delete(request);
console.log('Contact form synced successfully');
} catch (error) {
console.error('Failed to sync contact form:', error);
}
}
} catch (error) {
console.error('Background sync failed:', error);
}
}
// Push notification handling
self.addEventListener('push', event => {
console.log('Service Worker: Push received', event);
let data = {};
if (event.data) {
data = event.data.json();
}
const title = data.title || 'SmartSolTech';
const options = {
body: data.body || 'You have a new notification',
icon: '/images/icon-192x192.png',
badge: '/images/icon-72x72.png',
tag: data.tag || 'default',
data: data.url || '/',
actions: [
{
action: 'open',
title: '열기',
icon: '/images/icon-open.png'
},
{
action: 'close',
title: '닫기',
icon: '/images/icon-close.png'
}
],
requireInteraction: data.requireInteraction || false,
silent: data.silent || false,
vibrate: data.vibrate || [200, 100, 200]
};
event.waitUntil(
self.registration.showNotification(title, options)
);
});
// Notification click handling
self.addEventListener('notificationclick', event => {
console.log('Service Worker: Notification clicked', event);
event.notification.close();
if (event.action === 'close') {
return;
}
const url = event.notification.data || '/';
event.waitUntil(
clients.matchAll({ type: 'window' }).then(clientList => {
// Check if window is already open
for (const client of clientList) {
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// Open new window
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
// Handle messages from main thread
self.addEventListener('message', event => {
console.log('Service Worker: Message received', event.data);
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
if (event.data && event.data.type === 'CACHE_URLS') {
cacheUrls(event.data.urls);
}
});
async function cacheUrls(urls) {
try {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
await cache.addAll(urls);
console.log('URLs cached successfully:', urls);
} catch (error) {
console.error('Failed to cache URLs:', error);
}
}
// Periodic background sync (if supported)
self.addEventListener('periodicsync', event => {
console.log('Service Worker: Periodic sync triggered', event.tag);
if (event.tag === 'content-sync') {
event.waitUntil(syncContent());
}
});
async function syncContent() {
try {
// Fetch fresh portfolio and services data
const portfolioResponse = await fetch('/api/portfolio?featured=true');
const servicesResponse = await fetch('/api/services?featured=true');
if (portfolioResponse.ok && servicesResponse.ok) {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
cache.put('/api/portfolio?featured=true', portfolioResponse.clone());
cache.put('/api/services?featured=true', servicesResponse.clone());
console.log('Content synced successfully');
}
} catch (error) {
console.error('Content sync failed:', error);
}
}
// Cache management utilities
async function cleanupCaches() {
const cacheNames = await caches.keys();
const currentCaches = [STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME];
return Promise.all(
cacheNames.map(cacheName => {
if (!currentCaches.includes(cacheName)) {
console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
}
// Limit cache size
async function limitCacheSize(cacheName, maxItems) {
const cache = await caches.open(cacheName);
const keys = await cache.keys();
if (keys.length > maxItems) {
const keysToDelete = keys.slice(0, keys.length - maxItems);
return Promise.all(keysToDelete.map(key => cache.delete(key)));
}
}
// Performance monitoring
self.addEventListener('fetch', event => {
if (event.request.url.includes('/api/')) {
const start = performance.now();
event.respondWith(
fetch(event.request).then(response => {
const duration = performance.now() - start;
// Log slow API requests
if (duration > 2000) {
console.warn('Slow API request:', event.request.url, duration + 'ms');
}
return response;
})
);
}
});
// Error tracking
self.addEventListener('error', event => {
console.error('Service Worker error:', event.error);
// Could send to analytics service
});
self.addEventListener('unhandledrejection', event => {
console.error('Service Worker unhandled rejection:', event.reason);
// Could send to analytics service
});

View File

@@ -0,0 +1,414 @@
// Service Worker for SmartSolTech PWA
const CACHE_NAME = 'smartsoltech-v1.0.2';
const STATIC_CACHE_NAME = 'smartsoltech-static-v1.0.2';
const DYNAMIC_CACHE_NAME = 'smartsoltech-dynamic-v1.0.2';
// Files to cache immediately
const STATIC_FILES = [
'/',
'/css/main.css',
'/css/fixes.css',
'/css/dark-theme.css',
'/js/main.js',
'/vendor/jquery/jquery-3.6.0.min.js',
'/vendor/bootstrap/bootstrap.bundle.min.js',
'/vendor/bootstrap/bootstrap.min.css',
'/vendor/adminlte/adminlte.min.js',
'/vendor/adminlte/adminlte.min.css',
'/vendor/adminlte/fontawesome.min.css',
'/images/logo.png',
'/images/icon-192x192.png',
'/images/icon-144x144.png',
'/manifest.json',
'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap',
'https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap',
'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css',
'https://unpkg.com/aos@2.3.1/dist/aos.css',
'https://unpkg.com/aos@2.3.1/dist/aos.js'
];
// Routes to cache dynamically
const DYNAMIC_ROUTES = [
'/about',
'/services',
'/portfolio',
'/calculator',
'/contact'
];
// API endpoints to cache
const API_CACHE_PATTERNS = [
/^\/api\/portfolio/,
/^\/api\/services/,
/^\/api\/calculator\/services/
];
// Install event - cache static files
self.addEventListener('install', event => {
console.log('Service Worker: Installing...');
event.waitUntil(
caches.open(STATIC_CACHE_NAME)
.then(cache => {
console.log('Service Worker: Caching static files');
return cache.addAll(STATIC_FILES);
})
.then(() => {
console.log('Service Worker: Static files cached');
return self.skipWaiting();
})
.catch(error => {
console.error('Service Worker: Error caching static files', error);
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', event => {
console.log('Service Worker: Activating...');
event.waitUntil(
caches.keys()
.then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== STATIC_CACHE_NAME &&
cacheName !== DYNAMIC_CACHE_NAME) {
console.log('Service Worker: Deleting old cache', cacheName);
return caches.delete(cacheName);
}
})
);
})
.then(() => {
console.log('Service Worker: Activated');
return self.clients.claim();
})
);
});
// Fetch event - serve cached files or fetch from network
self.addEventListener('fetch', event => {
const request = event.request;
const url = new URL(request.url);
// Skip non-GET requests
if (request.method !== 'GET') {
return;
}
// Skip Chrome extension requests
if (url.protocol === 'chrome-extension:') {
return;
}
// Handle different types of requests
if (isStaticFile(request.url)) {
event.respondWith(cacheFirst(request));
} else if (isAPIRequest(request.url)) {
event.respondWith(networkFirst(request));
} else if (isDynamicRoute(request.url) || url.pathname === '/') {
// For main pages, always check network first to ensure language consistency
event.respondWith(networkFirst(request));
} else {
event.respondWith(networkFirst(request));
}
});
// Cache strategies
async function cacheFirst(request) {
try {
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
const networkResponse = await fetch(request);
const cache = await caches.open(STATIC_CACHE_NAME);
cache.put(request, networkResponse.clone());
return networkResponse;
} catch (error) {
console.error('Cache first strategy failed:', error);
return new Response('Offline', { status: 503 });
}
}
async function networkFirst(request) {
try {
const networkResponse = await fetch(request);
// Cache successful responses
if (networkResponse.ok) {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
console.log('Network first: Falling back to cache for', request.url);
const cachedResponse = await caches.match(request);
if (cachedResponse) {
return cachedResponse;
}
// Return offline page for navigation requests
if (request.mode === 'navigate') {
return caches.match('/offline.html') || new Response('Offline', {
status: 503,
headers: { 'Content-Type': 'text/html' }
});
}
return new Response('Network Error', { status: 503 });
}
}
async function staleWhileRevalidate(request) {
try {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
const cachedResponse = await cache.match(request);
const fetchPromise = fetch(request).then(networkResponse => {
if (networkResponse && networkResponse.ok) {
cache.put(request, networkResponse.clone());
}
return networkResponse;
}).catch(error => {
console.log('staleWhileRevalidate fetch failed:', error);
return null;
});
return cachedResponse || fetchPromise || new Response('Not available', { status: 503 });
} catch (error) {
console.error('staleWhileRevalidate error:', error);
return new Response('Service unavailable', { status: 503 });
}
}
// Helper functions
function isStaticFile(url) {
return url.includes('/css/') ||
url.includes('/js/') ||
url.includes('/images/') ||
url.includes('/fonts/') ||
url.includes('googleapis.com') ||
url.includes('cdnjs.cloudflare.com');
}
function isAPIRequest(url) {
return url.includes('/api/') ||
API_CACHE_PATTERNS.some(pattern => pattern.test(url));
}
function isDynamicRoute(url) {
const pathname = new URL(url).pathname;
return DYNAMIC_ROUTES.includes(pathname) ||
pathname.startsWith('/portfolio/') ||
pathname.startsWith('/services/');
}
// Background sync for form submissions
self.addEventListener('sync', event => {
console.log('Service Worker: Background sync triggered', event.tag);
if (event.tag === 'contact-form-sync') {
event.waitUntil(syncContactForms());
}
});
async function syncContactForms() {
try {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
const requests = await cache.keys();
const contactRequests = requests.filter(request =>
request.url.includes('/api/contact/submit')
);
for (const request of contactRequests) {
try {
await fetch(request);
await cache.delete(request);
console.log('Contact form synced successfully');
} catch (error) {
console.error('Failed to sync contact form:', error);
}
}
} catch (error) {
console.error('Background sync failed:', error);
}
}
// Push notification handling
self.addEventListener('push', event => {
console.log('Service Worker: Push received', event);
let data = {};
if (event.data) {
data = event.data.json();
}
const title = data.title || 'SmartSolTech';
const options = {
body: data.body || 'You have a new notification',
icon: '/images/icon-192x192.png',
badge: '/images/icon-72x72.png',
tag: data.tag || 'default',
data: data.url || '/',
actions: [
{
action: 'open',
title: '열기',
icon: '/images/icon-open.png'
},
{
action: 'close',
title: '닫기',
icon: '/images/icon-close.png'
}
],
requireInteraction: data.requireInteraction || false,
silent: data.silent || false,
vibrate: data.vibrate || [200, 100, 200]
};
event.waitUntil(
self.registration.showNotification(title, options)
);
});
// Notification click handling
self.addEventListener('notificationclick', event => {
console.log('Service Worker: Notification clicked', event);
event.notification.close();
if (event.action === 'close') {
return;
}
const url = event.notification.data || '/';
event.waitUntil(
clients.matchAll({ type: 'window' }).then(clientList => {
// Check if window is already open
for (const client of clientList) {
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// Open new window
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
// Handle messages from main thread
self.addEventListener('message', event => {
console.log('Service Worker: Message received', event.data);
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
if (event.data && event.data.type === 'CACHE_URLS') {
cacheUrls(event.data.urls);
}
});
async function cacheUrls(urls) {
try {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
await cache.addAll(urls);
console.log('URLs cached successfully:', urls);
} catch (error) {
console.error('Failed to cache URLs:', error);
}
}
// Periodic background sync (if supported)
self.addEventListener('periodicsync', event => {
console.log('Service Worker: Periodic sync triggered', event.tag);
if (event.tag === 'content-sync') {
event.waitUntil(syncContent());
}
});
async function syncContent() {
try {
// Fetch fresh portfolio and services data
const portfolioResponse = await fetch('/api/portfolio?featured=true');
const servicesResponse = await fetch('/api/services?featured=true');
if (portfolioResponse.ok && servicesResponse.ok) {
const cache = await caches.open(DYNAMIC_CACHE_NAME);
cache.put('/api/portfolio?featured=true', portfolioResponse.clone());
cache.put('/api/services?featured=true', servicesResponse.clone());
console.log('Content synced successfully');
}
} catch (error) {
console.error('Content sync failed:', error);
}
}
// Cache management utilities
async function cleanupCaches() {
const cacheNames = await caches.keys();
const currentCaches = [STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME];
return Promise.all(
cacheNames.map(cacheName => {
if (!currentCaches.includes(cacheName)) {
console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
}
// Limit cache size
async function limitCacheSize(cacheName, maxItems) {
const cache = await caches.open(cacheName);
const keys = await cache.keys();
if (keys.length > maxItems) {
const keysToDelete = keys.slice(0, keys.length - maxItems);
return Promise.all(keysToDelete.map(key => cache.delete(key)));
}
}
// Performance monitoring
self.addEventListener('fetch', event => {
if (event.request.url.includes('/api/')) {
const start = performance.now();
event.respondWith(
fetch(event.request).then(response => {
const duration = performance.now() - start;
// Log slow API requests
if (duration > 2000) {
console.warn('Slow API request:', event.request.url, duration + 'ms');
}
return response;
})
);
}
});
// Error tracking
self.addEventListener('error', event => {
console.error('Service Worker error:', event.error);
// Could send to analytics service
});
self.addEventListener('unhandledrejection', event => {
console.error('Service Worker unhandled rejection:', event.reason);
// Could send to analytics service
});