feat(cannaiq): Add PWA support with vite-plugin-pwa
- Add vite-plugin-pwa for service worker and manifest generation - Configure workbox for asset caching and API runtime caching - Add sharp for icon generation from SVG - Create generate-icons.js script to create 192x192 and 512x512 PNGs - Update build script to auto-generate icons before build App is now installable as a PWA with offline support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
"dev:admin": "vite --host --port 8080",
|
"dev:admin": "vite --host --port 8080",
|
||||||
"build": "tsc && vite build",
|
"generate-icons": "node scripts/generate-icons.js",
|
||||||
|
"build": "node scripts/generate-icons.js && tsc && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -26,6 +27,8 @@
|
|||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.0.8"
|
"vite": "^5.0.8",
|
||||||
|
"vite-plugin-pwa": "^0.21.1",
|
||||||
|
"sharp": "^0.33.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
cannaiq/scripts/generate-icons.js
Normal file
37
cannaiq/scripts/generate-icons.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Generate PWA icons from favicon.svg
|
||||||
|
*
|
||||||
|
* Run: node scripts/generate-icons.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
import { join, dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const publicDir = join(__dirname, '..', 'public');
|
||||||
|
|
||||||
|
const svgPath = join(publicDir, 'favicon.svg');
|
||||||
|
const svg = readFileSync(svgPath);
|
||||||
|
|
||||||
|
const sizes = [192, 512];
|
||||||
|
|
||||||
|
async function generateIcons() {
|
||||||
|
console.log('Generating PWA icons from favicon.svg...');
|
||||||
|
|
||||||
|
for (const size of sizes) {
|
||||||
|
const outputPath = join(publicDir, `icon-${size}.png`);
|
||||||
|
|
||||||
|
await sharp(svg)
|
||||||
|
.resize(size, size)
|
||||||
|
.png()
|
||||||
|
.toFile(outputPath);
|
||||||
|
|
||||||
|
console.log(` Created icon-${size}.png`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Done!');
|
||||||
|
}
|
||||||
|
|
||||||
|
generateIcons().catch(console.error);
|
||||||
@@ -1,8 +1,61 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [
|
||||||
|
react(),
|
||||||
|
VitePWA({
|
||||||
|
registerType: 'autoUpdate',
|
||||||
|
includeAssets: ['favicon.svg'],
|
||||||
|
manifest: {
|
||||||
|
name: 'CannaIQ',
|
||||||
|
short_name: 'CannaIQ',
|
||||||
|
description: 'Cannabis dispensary analytics and management',
|
||||||
|
theme_color: '#059669',
|
||||||
|
background_color: '#1f2937',
|
||||||
|
display: 'standalone',
|
||||||
|
scope: '/',
|
||||||
|
start_url: '/',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: '/icon-192.png',
|
||||||
|
sizes: '192x192',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/icon-512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: '/icon-512.png',
|
||||||
|
sizes: '512x512',
|
||||||
|
type: 'image/png',
|
||||||
|
purpose: 'maskable',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
workbox: {
|
||||||
|
// Cache all assets
|
||||||
|
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff,woff2}'],
|
||||||
|
// Runtime caching for API calls
|
||||||
|
runtimeCaching: [
|
||||||
|
{
|
||||||
|
urlPattern: /^https:\/\/cannaiq\.co\/api\/.*/i,
|
||||||
|
handler: 'NetworkFirst',
|
||||||
|
options: {
|
||||||
|
cacheName: 'api-cache',
|
||||||
|
expiration: {
|
||||||
|
maxEntries: 100,
|
||||||
|
maxAgeSeconds: 60 * 5, // 5 minutes
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
server: {
|
server: {
|
||||||
host: true,
|
host: true,
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
|||||||
Reference in New Issue
Block a user