@ -1,20 +1,323 @@
< template >
< template >
< div class = "status-view" >
< div class = "status-view" >
< div class = "view-header" >
< div class = "view-header" >
< h1 class = "page-title" > 📊 Status < / h1 >
< h1 class = "page-title" > 📊 Status Matrix < / h1 >
< p class = "page-subtitle" > System status and diagnostics < / p >
< p class = "page-subtitle" > Comprehensive system status and diagnostics < / p >
< / div >
<!-- Status Matrix Grid -- >
< div class = "status-matrix" >
< div class = "matrix-header" >
< h2 > Runtime Capabilities < / h2 >
< div class = "matrix-actions" >
< button
class = "action-button refresh"
@ click = "refreshStatus"
: disabled = "isRefreshing"
>
🔄 { { isRefreshing ? 'Refreshing...' : 'Refresh' } }
< / button >
< button
class = "action-button export"
@ click = "exportDiagnostics"
>
📋 Copy Diagnostics
< / button >
< / div >
< / div >
< div class = "matrix-grid" >
< StatusCard
v - for = "item in statusItems"
: key = "item.key"
: title = "item.title"
: status = "item.status"
: value = "item.value"
: description = "item.description"
: action = "item.action"
@ action - click = "handleAction"
/ >
< / div >
< / div >
<!-- Diagnostics Section -- >
< div class = "diagnostics-section" >
< h2 > System Diagnostics < / h2 >
< div class = "diagnostics-content" >
< div class = "diagnostics-info" >
< div class = "info-item" >
< span class = "label" > App Version : < / span >
< span class = "value" > { { diagnostics . appVersion } } < / span >
< / div >
< div class = "info-item" >
< span class = "label" > Platform : < / span >
< span class = "value" > { { diagnostics . platform } } < / span >
< / div >
< div class = "info-item" >
< span class = "label" > API Level : < / span >
< span class = "value" > { { diagnostics . apiLevel } } < / span >
< / div >
< div class = "info-item" >
< span class = "label" > Timezone : < / span >
< span class = "value" > { { diagnostics . timezone } } < / span >
< / div >
< div class = "info-item" >
< span class = "label" > Last Updated : < / span >
< span class = "value" > { { diagnostics . lastUpdated } } < / span >
< / div >
< / div >
< div class = "diagnostics-json" >
< h3 > Raw Diagnostics ( JSON ) < / h3 >
< pre class = "json-output" > { { diagnosticsJson } } < / pre >
< / div >
< / div >
< / div >
<!-- Error Display -- >
< div v-if ="errorMessage" class="error-section" >
< h2 > ⚠ ️ Error Information < / h2 >
< div class = "error-content" >
< p > { { errorMessage } } < / p >
< button class = "action-button" @click ="clearError" > Clear Error < / button >
< / div >
< / div >
< div class = "placeholder-content" >
< p > Status view coming soon ... < / p >
< / div >
< / div >
< / div >
< / div >
< / template >
< / template >
< script lang = "ts" >
< script lang = "ts" >
import { Vue , Component , toNative } from 'vue-facing-decorator'
import { Vue , Component , toNative } from 'vue-facing-decorator'
import StatusCard from '../components/cards/StatusCard.vue'
interface StatusItem {
key : string
title : string
status : 'success' | 'warning' | 'error' | 'info'
value : string
description : string
action ? : {
label : string
method : string
}
}
interface Diagnostics {
appVersion : string
platform : string
apiLevel : string
timezone : string
lastUpdated : string
postNotificationsGranted : boolean
exactAlarmGranted : boolean
channelEnabled : boolean
batteryOptimizationsIgnored : boolean
canScheduleNow : boolean
lastError ? : string
capabilities : Record < string , any >
}
@ Component ( {
components : {
StatusCard
}
} )
class StatusView extends Vue {
isRefreshing = false
errorMessage = ''
diagnostics : Diagnostics = {
appVersion : '1.0.0' ,
platform : 'Android' ,
apiLevel : 'Unknown' ,
timezone : 'Unknown' ,
lastUpdated : 'Never' ,
postNotificationsGranted : false ,
exactAlarmGranted : false ,
channelEnabled : false ,
batteryOptimizationsIgnored : false ,
canScheduleNow : false ,
capabilities : { }
}
get statusItems ( ) : StatusItem [ ] {
return [
{
key : 'postNotifications' ,
title : 'Post Notifications' ,
status : this . diagnostics . postNotificationsGranted ? 'success' : 'error' ,
value : this . diagnostics . postNotificationsGranted ? 'Granted' : 'Not Granted' ,
description : 'Permission to display notifications' ,
action : ! this . diagnostics . postNotificationsGranted ? {
label : 'Request Permission' ,
method : 'requestPermissions'
} : undefined
} ,
{
key : 'exactAlarm' ,
title : 'Exact Alarm' ,
status : this . diagnostics . exactAlarmGranted ? 'success' : 'warning' ,
value : this . diagnostics . exactAlarmGranted ? 'Granted' : 'Not Granted' ,
description : 'Permission for precise alarm scheduling' ,
action : ! this . diagnostics . exactAlarmGranted ? {
label : 'Open Settings' ,
method : 'openExactAlarmSettings'
} : undefined
} ,
{
key : 'channelEnabled' ,
title : 'Notification Channel' ,
status : this . diagnostics . channelEnabled ? 'success' : 'error' ,
value : this . diagnostics . channelEnabled ? 'Enabled' : 'Disabled' ,
description : 'Notification channel status' ,
action : ! this . diagnostics . channelEnabled ? {
label : 'Open Channel Settings' ,
method : 'openChannelSettings'
} : undefined
} ,
{
key : 'batteryOptimizations' ,
title : 'Battery Optimizations' ,
status : this . diagnostics . batteryOptimizationsIgnored ? 'success' : 'warning' ,
value : this . diagnostics . batteryOptimizationsIgnored ? 'Ignored' : 'Not Ignored' ,
description : 'Battery optimization exemption' ,
action : ! this . diagnostics . batteryOptimizationsIgnored ? {
label : 'Request Exemption' ,
method : 'requestBatteryOptimizationExemption'
} : undefined
} ,
{
key : 'canSchedule' ,
title : 'Can Schedule Now' ,
status : this . diagnostics . canScheduleNow ? 'success' : 'error' ,
value : this . diagnostics . canScheduleNow ? 'Yes' : 'No' ,
description : 'Ready to schedule notifications' ,
action : ! this . diagnostics . canScheduleNow ? {
label : 'Check Prerequisites' ,
method : 'checkPrerequisites'
} : undefined
}
]
}
get diagnosticsJson ( ) : string {
return JSON . stringify ( this . diagnostics , null , 2 )
}
async mounted ( ) {
await this . refreshStatus ( )
}
async refreshStatus ( ) {
this . isRefreshing = true
this . errorMessage = ''
try {
console . log ( '🔄 Refreshing status matrix...' )
/ / I m p o r t t h e p l u g i n d y n a m i c a l l y
const { DailyNotification } = await import ( '@timesafari/daily-notification-plugin' )
const plugin = DailyNotification
if ( ! plugin ) {
throw new Error ( 'DailyNotification plugin not available' )
}
/ / C o l l e c t c o m p r e h e n s i v e s t a t u s
const [ notificationStatus , permissions , exactAlarmStatus ] = await Promise . all ( [
plugin . getNotificationStatus ( ) . catch ( ( ) => ( { isEnabled : false , isScheduled : false } ) ) ,
plugin . checkPermissions ( ) . catch ( ( ) => ( { notifications : 'denied' } ) ) ,
plugin . getExactAlarmStatus ( ) . catch ( ( ) => ( { enabled : false , supported : false } ) )
] )
/ / U p d a t e d i a g n o s t i c s
this . diagnostics = {
appVersion : '1.0.0' , / / T O D O : G e t f r o m a p p i n f o
platform : 'Android' , / / T O D O : D e t e c t p l a t f o r m
apiLevel : 'Unknown' , / / T O D O : G e t f r o m d e v i c e i n f o
timezone : Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ,
lastUpdated : new Date ( ) . toLocaleString ( ) ,
postNotificationsGranted : permissions . notifications === 'granted' ,
exactAlarmGranted : exactAlarmStatus . enabled ,
channelEnabled : notificationStatus . isEnabled ,
batteryOptimizationsIgnored : false , / / T O D O : C h e c k b a t t e r y o p t i m i z a t i o n s t a t u s
canScheduleNow : notificationStatus . isEnabled && permissions . notifications === 'granted' ,
lastError : notificationStatus . error ,
capabilities : {
notificationStatus ,
permissions ,
exactAlarmStatus
}
}
console . log ( '✅ Status matrix refreshed successfully' )
} catch ( error ) {
console . error ( '❌ Failed to refresh status:' , error )
this . errorMessage = ` Failed to refresh status: ${ error . message } `
} finally {
this . isRefreshing = false
}
}
async handleAction ( action : { label : string ; method : string } ) {
try {
console . log ( ` 🔧 Executing action: ${ action . method } ` )
const { DailyNotification } = await import ( '@timesafari/daily-notification-plugin' )
const plugin = DailyNotification
switch ( action . method ) {
case 'requestPermissions' :
await plugin . requestPermissions ( )
break
case 'openExactAlarmSettings' :
await plugin . openExactAlarmSettings ( )
break
case 'openChannelSettings' :
await plugin . openChannelSettings ( )
break
case 'requestBatteryOptimizationExemption' :
await plugin . requestBatteryOptimizationExemption ( )
break
case 'checkPrerequisites' :
await this . refreshStatus ( )
break
default :
console . warn ( ` Unknown action method: ${ action . method } ` )
}
/ / R e f r e s h s t a t u s a f t e r a c t i o n
await this . refreshStatus ( )
} catch ( error ) {
console . error ( ` ❌ Action ${ action . method } failed: ` , error )
this . errorMessage = ` Action failed: ${ error . message } `
}
}
async exportDiagnostics ( ) {
try {
await navigator . clipboard . writeText ( this . diagnosticsJson )
console . log ( '📋 Diagnostics copied to clipboard' )
/ / S h o w s u c c e s s f e e d b a c k
const button = document . querySelector ( '.action-button.export' ) as HTMLButtonElement
const originalText = button . textContent
button . textContent = '✅ Copied!'
setTimeout ( ( ) => {
button . textContent = originalText
} , 2000 )
} catch ( error ) {
console . error ( '❌ Failed to copy diagnostics:' , error )
this . errorMessage = ` Failed to copy diagnostics: ${ error . message } `
}
}
clearError ( ) {
this . errorMessage = ''
}
}
@ Component
class StatusView extends Vue { }
export default toNative ( StatusView )
export default toNative ( StatusView )
< / script >
< / script >
@ -43,13 +346,220 @@ export default toNative(StatusView)
color : rgba ( 255 , 255 , 255 , 0.8 ) ;
color : rgba ( 255 , 255 , 255 , 0.8 ) ;
}
}
. placeholder - content {
/* Status Matrix */
. status - matrix {
background : rgba ( 255 , 255 , 255 , 0.1 ) ;
background : rgba ( 255 , 255 , 255 , 0.1 ) ;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.2 ) ;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.2 ) ;
border - radius : 12 px ;
border - radius : 12 px ;
padding : 40 px ;
padding : 24 px ;
text - align : center ;
margin - bottom : 24 px ;
backdrop - filter : blur ( 10 px ) ;
}
. matrix - header {
display : flex ;
justify - content : space - between ;
align - items : center ;
margin - bottom : 20 px ;
}
. matrix - header h2 {
margin : 0 ;
color : white ;
font - size : 20 px ;
font - weight : 600 ;
}
. matrix - actions {
display : flex ;
gap : 12 px ;
}
. action - button {
background : rgba ( 255 , 255 , 255 , 0.2 ) ;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.3 ) ;
color : white ;
padding : 8 px 16 px ;
border - radius : 8 px ;
cursor : pointer ;
font - size : 14 px ;
font - weight : 500 ;
transition : all 0.3 s ease ;
display : flex ;
align - items : center ;
gap : 6 px ;
}
. action - button : hover : not ( : disabled ) {
background : rgba ( 255 , 255 , 255 , 0.3 ) ;
transform : translateY ( - 1 px ) ;
}
. action - button : disabled {
opacity : 0.5 ;
cursor : not - allowed ;
transform : none ;
}
. action - button . refresh {
background : rgba ( 59 , 130 , 246 , 0.3 ) ;
border - color : rgba ( 59 , 130 , 246 , 0.5 ) ;
}
. action - button . export {
background : rgba ( 34 , 197 , 94 , 0.3 ) ;
border - color : rgba ( 34 , 197 , 94 , 0.5 ) ;
}
. matrix - grid {
display : grid ;
grid - template - columns : repeat ( auto - fit , minmax ( 300 px , 1 fr ) ) ;
gap : 16 px ;
}
/* Diagnostics Section */
. diagnostics - section {
background : rgba ( 255 , 255 , 255 , 0.1 ) ;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.2 ) ;
border - radius : 12 px ;
padding : 24 px ;
margin - bottom : 24 px ;
backdrop - filter : blur ( 10 px ) ;
}
. diagnostics - section h2 {
margin : 0 0 20 px 0 ;
color : white ;
font - size : 20 px ;
font - weight : 600 ;
}
. diagnostics - content {
display : grid ;
grid - template - columns : 1 fr 1 fr ;
gap : 24 px ;
}
. diagnostics - info {
display : flex ;
flex - direction : column ;
gap : 12 px ;
}
. info - item {
display : flex ;
justify - content : space - between ;
align - items : center ;
padding : 8 px 0 ;
border - bottom : 1 px solid rgba ( 255 , 255 , 255 , 0.1 ) ;
}
. info - item : last - child {
border - bottom : none ;
}
. info - item . label {
color : rgba ( 255 , 255 , 255 , 0.8 ) ;
color : rgba ( 255 , 255 , 255 , 0.8 ) ;
font - weight : 500 ;
}
. info - item . value {
color : white ;
font - weight : 600 ;
}
. diagnostics - json h3 {
margin : 0 0 12 px 0 ;
color : white ;
font - size : 16 px ;
font - weight : 600 ;
}
. json - output {
background : rgba ( 0 , 0 , 0 , 0.3 ) ;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.2 ) ;
border - radius : 8 px ;
padding : 16 px ;
color : # e5e7eb ;
font - family : 'Monaco' , 'Menlo' , 'Ubuntu Mono' , monospace ;
font - size : 12 px ;
line - height : 1.4 ;
overflow - x : auto ;
max - height : 300 px ;
overflow - y : auto ;
}
/* Error Section */
. error - section {
background : rgba ( 239 , 68 , 68 , 0.2 ) ;
border : 1 px solid rgba ( 239 , 68 , 68 , 0.4 ) ;
border - radius : 12 px ;
padding : 24 px ;
backdrop - filter : blur ( 10 px ) ;
backdrop - filter : blur ( 10 px ) ;
}
}
. error - section h2 {
margin : 0 0 16 px 0 ;
color : # fca5a5 ;
font - size : 18 px ;
font - weight : 600 ;
}
. error - content {
display : flex ;
justify - content : space - between ;
align - items : center ;
gap : 16 px ;
}
. error - content p {
margin : 0 ;
color : # fca5a5 ;
flex : 1 ;
}
/* Responsive Design */
@ media ( max - width : 768 px ) {
. status - view {
padding : 16 px ;
}
. matrix - header {
flex - direction : column ;
gap : 16 px ;
align - items : stretch ;
}
. matrix - actions {
justify - content : center ;
}
. diagnostics - content {
grid - template - columns : 1 fr ;
gap : 16 px ;
}
. error - content {
flex - direction : column ;
align - items : stretch ;
}
. matrix - grid {
grid - template - columns : 1 fr ;
}
}
@ media ( max - width : 480 px ) {
. page - title {
font - size : 24 px ;
}
. matrix - actions {
flex - direction : column ;
}
. action - button {
justify - content : center ;
}
}
< / style >
< / style >