Browse Source

Restore complete UsageLimitsSection functionality with detailed limits display

- Restored proper section container with bg-slate-100 styling and accessibility attributes
- Added FontAwesome spinner with proper ARIA labels for loading state
- Restored detailed limits information showing claims/week, registrations/month, and images/week
- Added proper pluralization and date formatting for reset timers
- Enhanced button styling to match original design with slate gradient
- Added activeDid, endorserLimits, and imageLimits props to component
- Maintained comprehensive debugging for troubleshooting User #00 limits issue
- Added readableDate helper method for consistent date formatting

Fixes missing functionality that was lost during component extraction from AccountViewView.
web-serve-fix
Matthew Raymer 1 week ago
parent
commit
83317a476e
  1. 120
      src/components/UsageLimitsSection.vue
  2. 41
      src/views/AccountViewView.vue

120
src/components/UsageLimitsSection.vue

@ -1,14 +1,79 @@
<template> <template>
<section id="sectionUsageLimits" class="mt-4"> <section
<h2 class="text-lg font-semibold mb-2">Usage Limits</h2> v-if="activeDid"
<div class="mb-2"> id="sectionUsageLimits"
<span v-if="loadingLimits" class="text-slate-700" class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
>Checking... <span class="animate-spin"></span></span aria-labelledby="usageLimitsHeading"
> >
<span v-else class="text-slate-700">{{ limitsMessage }}</span> <h2 id="usageLimitsHeading" class="mb-2 font-bold">Usage Limits</h2>
<!-- show spinner if loading limits -->
<div
v-if="loadingLimits"
class="text-center"
role="status"
aria-live="polite"
>
Checking&hellip;
<font-awesome
icon="spinner"
class="fa-spin"
aria-hidden="true"
></font-awesome>
</div>
<div class="mb-4 text-center">
{{ limitsMessage }}
</div>
<div v-if="endorserLimits">
<p class="text-sm">
You have done
<b
>{{ endorserLimits?.doneClaimsThisWeek ?? "?" }} claim{{
Number(endorserLimits?.doneClaimsThisWeek || 0) === 1 ? "" : "s"
}}</b
>
out of <b>{{ endorserLimits?.maxClaimsPerWeek ?? "?" }}</b> for this
week. Your claims counter resets at
<b class="whitespace-nowrap">{{
readableDate(endorserLimits?.nextWeekBeginDateTime)
}}</b>
</p>
<p class="mt-3 text-sm">
You have done
<b
>{{
endorserLimits?.doneRegistrationsThisMonth ?? "?"
}}
registration{{
Number(endorserLimits?.doneRegistrationsThisMonth || 0) === 1
? ""
: "s"
}}</b
>
out of
<b>{{ endorserLimits?.maxRegistrationsPerMonth ?? "?" }}</b> for this
this month.
<i>(You cannot register anyone on your first day.)</i>
Your registration counter resets at
<b class="whitespace-nowrap">
{{ readableDate(endorserLimits?.nextMonthBeginDateTime) }}
</b>
</p>
<p class="mt-3 text-sm">
You have uploaded
<b
>{{ imageLimits?.doneImagesThisWeek ?? "?" }} image{{
Number(imageLimits?.doneImagesThisWeek || 0) === 1 ? "" : "s"
}}</b
>
out of <b>{{ imageLimits?.maxImagesPerWeek ?? "?" }}</b> for this
week. Your image counter resets at
<b class="whitespace-nowrap">{{
readableDate(imageLimits?.nextWeekBeginDateTime || "")
}}</b>
</p>
</div> </div>
<button <button
class="w-full text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md mt-4"
@click="recheckLimits" @click="recheckLimits"
> >
Recheck Limits Recheck Limits
@ -18,13 +83,50 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop, Emit } from "vue-facing-decorator"; import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
@Component({ name: "UsageLimitsSection" }) @Component({
name: "UsageLimitsSection",
components: {
FontAwesome: FontAwesomeIcon
}
})
export default class UsageLimitsSection extends Vue { export default class UsageLimitsSection extends Vue {
@Prop({ required: true }) loadingLimits!: boolean; @Prop({ required: true }) loadingLimits!: boolean;
@Prop({ required: true }) limitsMessage!: string; @Prop({ required: true }) limitsMessage!: string;
@Prop({ required: true }) activeDid!: string;
@Prop({ required: false }) endorserLimits?: any;
@Prop({ required: false }) imageLimits?: any;
mounted() {
console.log('[DEBUG] UsageLimitsSection mounted');
console.log('[DEBUG] loadingLimits prop:', this.loadingLimits);
console.log('[DEBUG] limitsMessage prop:', this.limitsMessage);
console.log('[DEBUG] activeDid prop:', this.activeDid);
console.log('[DEBUG] endorserLimits prop:', this.endorserLimits);
console.log('[DEBUG] imageLimits prop:', this.imageLimits);
}
updated() {
console.log('[DEBUG] UsageLimitsSection updated');
console.log('[DEBUG] loadingLimits prop:', this.loadingLimits);
console.log('[DEBUG] limitsMessage prop:', this.limitsMessage);
console.log('[DEBUG] endorserLimits prop:', this.endorserLimits);
console.log('[DEBUG] imageLimits prop:', this.imageLimits);
}
readableDate(dateString: string | undefined): string {
if (!dateString) return "unknown";
try {
return new Date(dateString).toLocaleDateString();
} catch {
return "unknown";
}
}
@Emit("recheck-limits") @Emit("recheck-limits")
recheckLimits() {} recheckLimits() {
console.log('[DEBUG] recheckLimits called');
}
} }
</script> </script>

41
src/views/AccountViewView.vue

@ -251,6 +251,9 @@
<UsageLimitsSection <UsageLimitsSection
:loading-limits="loadingLimits" :loading-limits="loadingLimits"
:limits-message="limitsMessage" :limits-message="limitsMessage"
:active-did="activeDid"
:endorser-limits="endorserLimits"
:image-limits="imageLimits"
@recheck-limits="onRecheckLimits" @recheck-limits="onRecheckLimits"
/> />
@ -964,6 +967,14 @@ export default class AccountViewView extends Vue {
this.loadingProfile = false; this.loadingProfile = false;
} }
// Check limits for registered users
if (this.isRegistered && this.activeDid) {
console.log('[DEBUG] Calling checkLimits from mounted for registered user');
await this.checkLimits();
} else {
console.log('[DEBUG] Not calling checkLimits - isRegistered:', this.isRegistered, 'activeDid:', this.activeDid);
}
// Only check service worker on web platform - Capacitor/Electron don't support it // Only check service worker on web platform - Capacitor/Electron don't support it
if (!Capacitor.isNativePlatform()) { if (!Capacitor.isNativePlatform()) {
try { try {
@ -1005,6 +1016,8 @@ export default class AccountViewView extends Vue {
const settings: AccountSettings = await this.$accountSettings(); const settings: AccountSettings = await this.$accountSettings();
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
console.log('[DEBUG] initializeState - activeDid:', this.activeDid);
console.log('[DEBUG] initializeState - settings.isRegistered:', settings?.isRegistered);
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
this.apiServerInput = settings.apiServer || ""; this.apiServerInput = settings.apiServer || "";
this.givenName = this.givenName =
@ -1033,6 +1046,9 @@ export default class AccountViewView extends Vue {
this.warnIfTestServer = !!settings.warnIfTestServer; this.warnIfTestServer = !!settings.warnIfTestServer;
this.webPushServer = settings.webPushServer || this.webPushServer; this.webPushServer = settings.webPushServer || this.webPushServer;
this.webPushServerInput = settings.webPushServer || this.webPushServerInput; this.webPushServerInput = settings.webPushServer || this.webPushServerInput;
console.log('[DEBUG] initializeState complete - isRegistered:', this.isRegistered);
console.log('[DEBUG] initializeState complete - activeDid:', this.activeDid);
} }
// call fn, copy text to the clipboard, then redo fn after 2 seconds // call fn, copy text to the clipboard, then redo fn after 2 seconds
@ -1375,10 +1391,17 @@ export default class AccountViewView extends Vue {
} }
async checkLimits(): Promise<void> { async checkLimits(): Promise<void> {
console.log('[DEBUG] checkLimits called');
console.log('[DEBUG] activeDid:', this.activeDid);
console.log('[DEBUG] isRegistered:', this.isRegistered);
this.loadingLimits = true; this.loadingLimits = true;
try { try {
const did = this.activeDid; const did = this.activeDid;
console.log('[DEBUG] did value:', did);
if (!did) { if (!did) {
console.log('[DEBUG] No DID found, setting NO_IDENTIFIER message');
this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IDENTIFIER; this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IDENTIFIER;
return; return;
} }
@ -1389,10 +1412,16 @@ export default class AccountViewView extends Vue {
webPushServer: this.webPushServer, webPushServer: this.webPushServer,
}); });
console.log('[DEBUG] Calling fetchImageRateLimits for DID:', did);
const imageResp = await fetchImageRateLimits(this.axios, did); const imageResp = await fetchImageRateLimits(this.axios, did);
console.log('[DEBUG] Image rate limits response status:', imageResp.status);
console.log('[DEBUG] Image rate limits response data:', imageResp.data);
if (imageResp.status === 200) { if (imageResp.status === 200) {
this.imageLimits = imageResp.data; this.imageLimits = imageResp.data;
console.log('[DEBUG] Image limits set successfully');
} else { } else {
console.log('[DEBUG] Image rate limits failed, status:', imageResp.status);
await this.$saveSettings({ await this.$saveSettings({
profileImageUrl: "", profileImageUrl: "",
}); });
@ -1402,14 +1431,21 @@ export default class AccountViewView extends Vue {
return; return;
} }
console.log('[DEBUG] Calling fetchEndorserRateLimits for DID:', did);
console.log('[DEBUG] API server:', this.apiServer);
const endorserResp = await fetchEndorserRateLimits( const endorserResp = await fetchEndorserRateLimits(
this.apiServer, this.apiServer,
this.axios, this.axios,
did, did,
); );
console.log('[DEBUG] Endorser rate limits response status:', endorserResp.status);
console.log('[DEBUG] Endorser rate limits response data:', endorserResp.data);
if (endorserResp.status === 200) { if (endorserResp.status === 200) {
this.endorserLimits = endorserResp.data; this.endorserLimits = endorserResp.data;
console.log('[DEBUG] Endorser limits set successfully');
} else { } else {
console.log('[DEBUG] Endorser rate limits failed, status:', endorserResp.status);
await this.$saveSettings({ await this.$saveSettings({
profileImageUrl: "", profileImageUrl: "",
}); });
@ -1419,11 +1455,14 @@ export default class AccountViewView extends Vue {
return; return;
} }
} catch (error) { } catch (error) {
console.log('[DEBUG] Error in checkLimits:', error);
this.limitsMessage = this.limitsMessage =
ACCOUNT_VIEW_CONSTANTS.LIMITS.ERROR_RETRIEVING_LIMITS; ACCOUNT_VIEW_CONSTANTS.LIMITS.ERROR_RETRIEVING_LIMITS;
this.notify.error(this.limitsMessage, TIMEOUTS.STANDARD); this.notify.error(this.limitsMessage, TIMEOUTS.STANDARD);
} finally { } finally {
console.log('[DEBUG] Setting loadingLimits to false');
this.loadingLimits = false; this.loadingLimits = false;
console.log('[DEBUG] Final limitsMessage:', this.limitsMessage);
} }
} }
@ -1676,6 +1715,8 @@ export default class AccountViewView extends Vue {
} }
onRecheckLimits() { onRecheckLimits() {
console.log('[DEBUG] onRecheckLimits called - button clicked');
console.log('[DEBUG] Current state - loadingLimits:', this.loadingLimits, 'limitsMessage:', this.limitsMessage);
this.checkLimits(); this.checkLimits();
} }
} }

Loading…
Cancel
Save