forked from trent_larson/crowd-funder-for-time-pwa
Migrate OfferDetailsView.vue to PlatformServiceMixin, notification constants, and template streamlining
- Replaced all databaseUtil and direct PlatformServiceFactory usage with PlatformServiceMixin methods - Abstracted all notification messages to src/constants/notifications.ts and migrated to notify helper - Added computed properties for assignment labels to streamline template logic - Removed unused imports and resolved all linter errors - Updated migration documentation and ensured security audit compliance - All changes validated with lint-fix and ready for human testing
This commit is contained in:
@@ -200,6 +200,92 @@ export const NOTIFY_REGISTER_NOT_AVAILABLE = {
|
||||
message: "You must get registered before you can create invites.",
|
||||
};
|
||||
|
||||
// OfferDetailsView.vue specific constants
|
||||
// Used in: OfferDetailsView.vue (mounted method - error loading offer details)
|
||||
export const NOTIFY_OFFER_ERROR_LOADING = {
|
||||
title: "Error",
|
||||
message: "There was an error loading the offer details.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (loadPreviousOffer method - previous record error)
|
||||
export const NOTIFY_OFFER_ERROR_PREVIOUS_RECORD = {
|
||||
title: "Retrieval Error",
|
||||
message:
|
||||
"The previous record isn't available for editing. If you submit, you'll create a new record.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (confirm method - no identifier error)
|
||||
export const NOTIFY_OFFER_ERROR_NO_IDENTIFIER = {
|
||||
title: "Error",
|
||||
message: "You must select an identifier before you can record a offer.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (confirm method - negative amount error)
|
||||
export const NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT = {
|
||||
title: "",
|
||||
message: "You may not send a negative number.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (confirm method - no description error)
|
||||
export const NOTIFY_OFFER_ERROR_NO_DESCRIPTION = {
|
||||
title: "Error",
|
||||
message: "You must enter a description or some number of {unit}.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (confirm method - processing status)
|
||||
export const NOTIFY_OFFER_PROCESSING = {
|
||||
title: "",
|
||||
message: "Recording the offer...",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (notifyUserOfProject method - no project error)
|
||||
export const NOTIFY_OFFER_ERROR_PROJECT_ASSIGNMENT = {
|
||||
title: "Error",
|
||||
message: "To assign to a project, you must open this page through a project.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (notifyUserOfProject method - conflict error)
|
||||
export const NOTIFY_OFFER_ERROR_PROJECT_RECIPIENT_CONFLICT = {
|
||||
title: "Error",
|
||||
message: "You cannot assign both to a project and to a recipient.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (notifyUserOfRecipient method - no recipient error)
|
||||
export const NOTIFY_OFFER_ERROR_RECIPIENT_ASSIGNMENT = {
|
||||
title: "Error",
|
||||
message: "To assign to a recipient, you must open this page from a contact.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (notifyUserOfRecipient method - conflict error)
|
||||
export const NOTIFY_OFFER_ERROR_RECIPIENT_PROJECT_CONFLICT = {
|
||||
title: "Error",
|
||||
message: "You cannot assign both to a recipient and to a project.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (recordOffer method - creation error)
|
||||
export const NOTIFY_OFFER_ERROR_CREATION = {
|
||||
title: "Error",
|
||||
message: "There was an error creating the offer.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (recordOffer method - success)
|
||||
export const NOTIFY_OFFER_SUCCESS_RECORDED = {
|
||||
title: "Success",
|
||||
message: "That offer was recorded.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (recordOffer method - recordation error)
|
||||
export const NOTIFY_OFFER_ERROR_RECORDATION = {
|
||||
title: "Error",
|
||||
message: "There was an error recording the offer.",
|
||||
};
|
||||
|
||||
// Used in: OfferDetailsView.vue (explainData method - privacy info)
|
||||
export const NOTIFY_OFFER_PRIVACY_INFO = {
|
||||
title: "Data Sharing",
|
||||
message: "Your data is shared with the world when you sign and send.",
|
||||
};
|
||||
|
||||
// Used in: [Component usage not yet documented]
|
||||
export const NOTIFY_REGISTER_PROCESSING = {
|
||||
title: "Processing",
|
||||
|
||||
@@ -21,16 +21,7 @@
|
||||
<h1 class="text-4xl text-center font-light px-4 mb-4">What Is Offered</h1>
|
||||
|
||||
<h1 class="text-xl font-bold text-center mb-4">
|
||||
<span>
|
||||
Offer to
|
||||
{{
|
||||
offeredToProject
|
||||
? projectName
|
||||
: offeredToRecipient
|
||||
? recipientName
|
||||
: "someone not named"
|
||||
}}</span
|
||||
>
|
||||
<span> Offer to {{ recipientDisplayName }} </span>
|
||||
</h1>
|
||||
<textarea
|
||||
v-model="descriptionOfItem"
|
||||
@@ -105,11 +96,7 @@
|
||||
@click="notifyUserOfProject()"
|
||||
/>
|
||||
<label class="text-sm mt-1">
|
||||
{{
|
||||
projectId
|
||||
? "This is offered to " + projectName
|
||||
: "No project was chosen"
|
||||
}}
|
||||
{{ projectAssignmentLabel }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -127,11 +114,7 @@
|
||||
@click="notifyUserOfRecipient()"
|
||||
/>
|
||||
<label class="text-sm mt-1">
|
||||
{{
|
||||
recipientDid
|
||||
? "This is offered to " + recipientName
|
||||
: "No recipient was chosen."
|
||||
}}
|
||||
{{ recipientAssignmentLabel }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -192,9 +175,24 @@ import {
|
||||
import * as libsUtil from "../libs/util";
|
||||
import { retrieveAccountDids } from "../libs/util";
|
||||
import { logger } from "../utils/logger";
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import * as databaseUtil from "../db/databaseUtil";
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
|
||||
import {
|
||||
NOTIFY_OFFER_ERROR_LOADING,
|
||||
NOTIFY_OFFER_ERROR_PREVIOUS_RECORD,
|
||||
NOTIFY_OFFER_ERROR_NO_IDENTIFIER,
|
||||
NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT,
|
||||
NOTIFY_OFFER_ERROR_NO_DESCRIPTION,
|
||||
NOTIFY_OFFER_PROCESSING,
|
||||
NOTIFY_OFFER_ERROR_PROJECT_ASSIGNMENT,
|
||||
NOTIFY_OFFER_ERROR_PROJECT_RECIPIENT_CONFLICT,
|
||||
NOTIFY_OFFER_ERROR_RECIPIENT_ASSIGNMENT,
|
||||
NOTIFY_OFFER_ERROR_RECIPIENT_PROJECT_CONFLICT,
|
||||
NOTIFY_OFFER_ERROR_CREATION,
|
||||
NOTIFY_OFFER_SUCCESS_RECORDED,
|
||||
NOTIFY_OFFER_ERROR_RECORDATION,
|
||||
NOTIFY_OFFER_PRIVACY_INFO,
|
||||
} from "@/constants/notifications";
|
||||
|
||||
/**
|
||||
* Offer Details View Component
|
||||
@@ -233,6 +231,7 @@ import * as databaseUtil from "../db/databaseUtil";
|
||||
QuickNav,
|
||||
TopMessage,
|
||||
},
|
||||
mixins: [PlatformServiceMixin],
|
||||
})
|
||||
export default class OfferDetailsView extends Vue {
|
||||
/** Notification function injected by Vue */
|
||||
@@ -241,6 +240,8 @@ export default class OfferDetailsView extends Vue {
|
||||
$route!: RouteLocationNormalizedLoaded;
|
||||
/** Router instance for navigation */
|
||||
$router!: Router;
|
||||
/** Notification helper methods */
|
||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||
|
||||
/** Currently active DID */
|
||||
activeDid = "";
|
||||
@@ -286,6 +287,45 @@ export default class OfferDetailsView extends Vue {
|
||||
/** Utility library reference */
|
||||
libsUtil = libsUtil;
|
||||
|
||||
/**
|
||||
* Component lifecycle hook that initializes notification helpers
|
||||
*/
|
||||
created() {
|
||||
this.notify = createNotifyHelpers(this.$notify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed property for recipient display name
|
||||
* Streamlines template logic for recipient/project display
|
||||
*/
|
||||
get recipientDisplayName() {
|
||||
return this.offeredToProject
|
||||
? this.projectName
|
||||
: this.offeredToRecipient
|
||||
? this.recipientName
|
||||
: "someone not named";
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed property for project assignment label
|
||||
* Streamlines template logic for project checkbox label
|
||||
*/
|
||||
get projectAssignmentLabel() {
|
||||
return this.projectId
|
||||
? `This is offered to ${this.projectName}`
|
||||
: "No project was chosen";
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed property for recipient assignment label
|
||||
* Streamlines template logic for recipient checkbox label
|
||||
*/
|
||||
get recipientAssignmentLabel() {
|
||||
return this.recipientDid
|
||||
? `This is offered to ${this.recipientName}`
|
||||
: "No recipient was chosen.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Component lifecycle hook that initializes the offer form
|
||||
*
|
||||
@@ -308,16 +348,9 @@ export default class OfferDetailsView extends Vue {
|
||||
await this.loadProjectInfo();
|
||||
} catch (err: unknown) {
|
||||
logger.error("Error in mounted:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text:
|
||||
(err as Error)?.message ||
|
||||
"There was an error loading the offer details.",
|
||||
},
|
||||
5000,
|
||||
this.notify.error(
|
||||
(err as Error)?.message || NOTIFY_OFFER_ERROR_LOADING.message,
|
||||
TIMEOUTS.LONG,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -334,14 +367,9 @@ export default class OfferDetailsView extends Vue {
|
||||
) as GenericCredWrapper<OfferClaim>)
|
||||
: undefined;
|
||||
} catch (error: unknown) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Retrieval Error",
|
||||
text: "The previous record isn't available for editing. If you submit, you'll create a new record.",
|
||||
},
|
||||
5000,
|
||||
this.notify.error(
|
||||
NOTIFY_OFFER_ERROR_PREVIOUS_RECORD.message,
|
||||
TIMEOUTS.LONG,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -403,7 +431,7 @@ export default class OfferDetailsView extends Vue {
|
||||
* @throws Will not throw but logs errors
|
||||
*/
|
||||
private async loadAccountSettings() {
|
||||
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
||||
const settings = await this.$accountSettings();
|
||||
this.apiServer = settings.apiServer ?? "";
|
||||
this.activeDid = settings.activeDid ?? "";
|
||||
this.showGeneralAdvanced = settings.showGeneralAdvanced ?? false;
|
||||
@@ -414,14 +442,7 @@ export default class OfferDetailsView extends Vue {
|
||||
*/
|
||||
private async loadRecipientInfo() {
|
||||
if (this.recipientDid && !this.recipientName) {
|
||||
let allContacts: Contact[] = [];
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
const queryResult = await platformService.dbQuery(
|
||||
"SELECT * FROM contacts",
|
||||
);
|
||||
allContacts = databaseUtil.mapQueryResultToValues(
|
||||
queryResult,
|
||||
) as unknown as Contact[];
|
||||
const allContacts = await this.$getAllContacts();
|
||||
const allMyDids = await retrieveAccountDids();
|
||||
this.recipientName = didInfo(
|
||||
this.recipientDid,
|
||||
@@ -526,53 +547,29 @@ export default class OfferDetailsView extends Vue {
|
||||
*/
|
||||
async confirm() {
|
||||
if (!this.activeDid) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: "You must select an identifier before you can record a offer.",
|
||||
},
|
||||
2000,
|
||||
this.notify.error(
|
||||
NOTIFY_OFFER_ERROR_NO_IDENTIFIER.message,
|
||||
TIMEOUTS.SHORT,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (parseFloat(this.amountInput) < 0) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
text: "You may not send a negative number.",
|
||||
title: "",
|
||||
},
|
||||
2000,
|
||||
this.notify.error(
|
||||
NOTIFY_OFFER_ERROR_NEGATIVE_AMOUNT.message,
|
||||
TIMEOUTS.SHORT,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.descriptionOfItem && !parseFloat(this.amountInput)) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: `You must enter a description or some number of ${
|
||||
this.libsUtil.UNIT_LONG[this.unitCode]
|
||||
}.`,
|
||||
},
|
||||
2000,
|
||||
const message = NOTIFY_OFFER_ERROR_NO_DESCRIPTION.message.replace(
|
||||
"{unit}",
|
||||
this.libsUtil.UNIT_LONG[this.unitCode],
|
||||
);
|
||||
this.notify.error(message, TIMEOUTS.SHORT);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "toast",
|
||||
text: "Recording the offer...",
|
||||
title: "",
|
||||
},
|
||||
1000,
|
||||
);
|
||||
this.notify.toast("", NOTIFY_OFFER_PROCESSING.message, TIMEOUTS.SHORT);
|
||||
|
||||
// this is asynchronous, but we don't need to wait for it to complete
|
||||
await this.recordOffer();
|
||||
@@ -589,25 +586,15 @@ export default class OfferDetailsView extends Vue {
|
||||
*/
|
||||
notifyUserOfProject() {
|
||||
if (!this.projectId) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "To assign to a project, you must open this page through a project.",
|
||||
},
|
||||
3000,
|
||||
this.notify.warning(
|
||||
NOTIFY_OFFER_ERROR_PROJECT_ASSIGNMENT.message,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
} else {
|
||||
// must be because offeredToRecipient is true
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "You cannot assign both to a project and to a recipient.",
|
||||
},
|
||||
3000,
|
||||
this.notify.warning(
|
||||
NOTIFY_OFFER_ERROR_PROJECT_RECIPIENT_CONFLICT.message,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -623,25 +610,15 @@ export default class OfferDetailsView extends Vue {
|
||||
*/
|
||||
notifyUserOfRecipient() {
|
||||
if (!this.recipientDid) {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "To assign to a recipient, you must open this page from a contact.",
|
||||
},
|
||||
3000,
|
||||
this.notify.warning(
|
||||
NOTIFY_OFFER_ERROR_RECIPIENT_ASSIGNMENT.message,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
} else {
|
||||
// must be because offeredToProject is true
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Error",
|
||||
text: "You cannot assign both to a recipient and to a project.",
|
||||
},
|
||||
3000,
|
||||
this.notify.warning(
|
||||
NOTIFY_OFFER_ERROR_RECIPIENT_PROJECT_CONFLICT.message,
|
||||
TIMEOUTS.STANDARD,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -700,24 +677,14 @@ export default class OfferDetailsView extends Vue {
|
||||
if (!result.success) {
|
||||
const errorMessage = this.getCreationErrorMessage(result);
|
||||
logger.error("Error with offer creation result:", result);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: errorMessage || "There was an error creating the offer.",
|
||||
},
|
||||
5000,
|
||||
this.notify.error(
|
||||
errorMessage || NOTIFY_OFFER_ERROR_CREATION.message,
|
||||
TIMEOUTS.LONG,
|
||||
);
|
||||
} else {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Success",
|
||||
text: `That offer was recorded.`,
|
||||
},
|
||||
5000,
|
||||
this.notify.success(
|
||||
NOTIFY_OFFER_SUCCESS_RECORDED.message,
|
||||
TIMEOUTS.LONG,
|
||||
);
|
||||
localStorage.removeItem("imageUrl");
|
||||
if (this.destinationPathAfter) {
|
||||
@@ -732,16 +699,8 @@ export default class OfferDetailsView extends Vue {
|
||||
const errorMessage =
|
||||
error.userMessage ||
|
||||
error.response?.data?.error?.message ||
|
||||
"There was an error recording the offer.";
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text: errorMessage,
|
||||
},
|
||||
5000,
|
||||
);
|
||||
NOTIFY_OFFER_ERROR_RECORDATION.message;
|
||||
this.notify.error(errorMessage, TIMEOUTS.LONG);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,15 +770,7 @@ export default class OfferDetailsView extends Vue {
|
||||
* @emits Notification with privacy message
|
||||
*/
|
||||
explainData() {
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "success",
|
||||
title: "Data Sharing",
|
||||
text: libsUtil.PRIVACY_MESSAGE,
|
||||
},
|
||||
7000,
|
||||
);
|
||||
this.notify.success(NOTIFY_OFFER_PRIVACY_INFO.message, TIMEOUTS.VERY_LONG);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user