feat: Implement comprehensive PWA functionality
- Add full PWA configuration with manifest and service worker - Create PWAInstallBanner component with mobile detection - Implement install banner on login page (shows below login form) - Add usePWA composable for reusable PWA functionality - Configure offline support with Workbox caching strategies - Add PWA initialization plugin - Update app name to 'Port Nimara Portal' throughout - Use circular logo in install banner and instructions - Banner shows only once and hides if already installed - Support both Android (direct install) and iOS (manual instructions) - Add comprehensive documentation for PWA implementation Features: - Mobile-only install banner with dismissal tracking - Standalone mode detection to hide banner when installed - Platform-specific installation instructions - Offline functionality with API caching - Auto-updating service worker - Native app-like experience when installed
This commit is contained in:
127
pages/login.vue
127
pages/login.vue
@@ -3,71 +3,76 @@
|
||||
<v-main class="container">
|
||||
<v-container class="fill-height" fluid>
|
||||
<v-row align="center" justify="center" class="fill-height">
|
||||
<v-card class="pa-6" rounded max-width="350" elevation="2">
|
||||
<v-form @submit.prevent="submit" v-model="valid">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="12">
|
||||
<v-img src="/logo.jpg" width="200" class="mb-3 mx-auto" />
|
||||
</v-col>
|
||||
<v-scroll-y-transition>
|
||||
<v-col v-if="errorThrown" cols="12" class="my-3">
|
||||
<v-alert
|
||||
text="Invalid email address or password"
|
||||
color="error"
|
||||
variant="tonal"
|
||||
/>
|
||||
<v-col cols="12" class="d-flex flex-column align-center">
|
||||
<v-card class="pa-6" rounded max-width="350" elevation="2">
|
||||
<v-form @submit.prevent="submit" v-model="valid">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="12">
|
||||
<v-img src="/logo.jpg" width="200" class="mb-3 mx-auto" />
|
||||
</v-col>
|
||||
</v-scroll-y-transition>
|
||||
<v-col cols="12">
|
||||
<v-row dense>
|
||||
<v-col cols="12" class="mt-4">
|
||||
<v-text-field
|
||||
v-model="emailAddress"
|
||||
placeholder="Email address"
|
||||
:disabled="loading"
|
||||
:rules="[
|
||||
(value) => !!value || 'Must not be empty',
|
||||
(value) =>
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ||
|
||||
'Invalid email address',
|
||||
]"
|
||||
variant="outlined"
|
||||
type="email"
|
||||
autofocus
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
@click:append-inner="passwordVisible = !passwordVisible"
|
||||
v-model="password"
|
||||
placeholder="Password"
|
||||
:disabled="loading"
|
||||
:type="passwordVisible ? 'text' : 'password'"
|
||||
:append-inner-icon="
|
||||
passwordVisible ? 'mdi-eye' : 'mdi-eye-off'
|
||||
"
|
||||
:rules="[(value) => !!value || 'Must not be empty']"
|
||||
autocomplete="current-password"
|
||||
variant="outlined"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-btn
|
||||
text="Log in"
|
||||
:disabled="!valid"
|
||||
:loading="loading"
|
||||
type="submit"
|
||||
<v-scroll-y-transition>
|
||||
<v-col v-if="errorThrown" cols="12" class="my-3">
|
||||
<v-alert
|
||||
text="Invalid email address or password"
|
||||
color="error"
|
||||
variant="tonal"
|
||||
color="primary"
|
||||
size="large"
|
||||
block
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card>
|
||||
</v-scroll-y-transition>
|
||||
<v-col cols="12">
|
||||
<v-row dense>
|
||||
<v-col cols="12" class="mt-4">
|
||||
<v-text-field
|
||||
v-model="emailAddress"
|
||||
placeholder="Email address"
|
||||
:disabled="loading"
|
||||
:rules="[
|
||||
(value) => !!value || 'Must not be empty',
|
||||
(value) =>
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ||
|
||||
'Invalid email address',
|
||||
]"
|
||||
variant="outlined"
|
||||
type="email"
|
||||
autofocus
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
@click:append-inner="passwordVisible = !passwordVisible"
|
||||
v-model="password"
|
||||
placeholder="Password"
|
||||
:disabled="loading"
|
||||
:type="passwordVisible ? 'text' : 'password'"
|
||||
:append-inner-icon="
|
||||
passwordVisible ? 'mdi-eye' : 'mdi-eye-off'
|
||||
"
|
||||
:rules="[(value) => !!value || 'Must not be empty']"
|
||||
autocomplete="current-password"
|
||||
variant="outlined"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-btn
|
||||
text="Log in"
|
||||
:disabled="!valid"
|
||||
:loading="loading"
|
||||
type="submit"
|
||||
variant="tonal"
|
||||
color="primary"
|
||||
size="large"
|
||||
block
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card>
|
||||
|
||||
<!-- PWA Install Banner -->
|
||||
<PWAInstallBanner />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-main>
|
||||
|
||||
Reference in New Issue
Block a user