+ @click="clickCard($event, plugin)">
| null;
}
export default defineComponent({
@@ -120,6 +139,10 @@ export default defineComponent({
type: Boolean,
required: true,
},
+ hasSomeAdminAccess: {
+ type: Boolean,
+ required: true,
+ },
activateNonce: {
type: String,
required: true,
@@ -140,9 +163,11 @@ export default defineComponent({
data(): PluginListState {
return {
showStartFreeTrialForPlugin: '',
+ showPluginDetailsForPlugin: null,
};
},
components: {
+ PluginDetailsModal,
CTAContainer,
StartFreeTrial,
},
@@ -225,26 +250,21 @@ export default defineComponent({
}
});
},
- clickCard(event: MouseEvent) {
+ clickCard(event: MouseEvent, plugin: Record
) {
// check if the target is a link or is a descendant of a link
// to skip direct clicks on links within the card, we want those honoured
- if ($(event.target as HTMLElement).closest('a').length) {
+ if ($(event.target as HTMLElement).closest('a:not(.card-title-link)').length) {
return;
}
- const titleLink = $(event.target as HTMLElement)
- .closest('.card-holder')
- .find('a.card-title-link')
- .get(0);
-
- if (titleLink) {
- event.stopPropagation();
-
- // jQuery dispatching can result in the new event having the .card-holder
- // as the event target, resulting in an endless dispatch cycle
- // Using a native event without bubbling circumvents this issue
- titleLink.dispatchEvent(new Event('click', { bubbles: false }));
- }
+ event.stopPropagation();
+ this.openDetailsModal(plugin);
+ },
+ openDetailsModal(plugin: Record) {
+ this.showPluginDetailsForPlugin = plugin;
+ },
+ startTrialFromDetailsModal(pluginName: string) {
+ this.showStartFreeTrialForPlugin = pluginName;
},
},
});
diff --git a/plugins/Marketplace/vue/src/types.ts b/plugins/Marketplace/vue/src/types.ts
new file mode 100644
index 00000000000..07d8d638f0a
--- /dev/null
+++ b/plugins/Marketplace/vue/src/types.ts
@@ -0,0 +1,92 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+import { iframeResizer } from 'iframe-resizer';
+
+export type TObject = Record | Record;
+export type TObjectArray = TObject[] | [] | null;
+export type TNumberOrString = string | number | null;
+
+export interface IPluginShopVariation {
+ price: TNumberOrString;
+ prettyPrice: string;
+ currency: string;
+ period: string;
+ name: string;
+ discount: TNumberOrString;
+ prettyDiscount: string;
+ addToCartUrl: string;
+ addToCartEmbedUrl: string;
+ cheapest: boolean | undefined;
+ recommended: boolean | undefined;
+}
+
+export interface IPluginShopReviews {
+ embedUrl: string;
+ height: number;
+ averageRating: TNumberOrString;
+ ratingCount: number;
+ reviewCount: number;
+}
+
+export interface IPluginShopDetails {
+ url: string;
+ variations: IPluginShopVariation[];
+ reviews: IPluginShopReviews;
+}
+
+export interface PluginDetails {
+ name: string;
+ displayName: string;
+ owner: string;
+ description: string;
+ homepage: string | null;
+ createdDateTime: string | unknown; // "2017-05-17 06:34:21"
+ donate: [];
+ support: [];
+ isTheme: boolean;
+ keywords: string[];
+ basePrice: number;
+ authors: TObjectArray;
+ repositoryUrl: string | null;
+ lastUpdated: string,
+ latestVersion: string
+ numDownloads: number | null;
+ screenshots: string[];
+ previews: TObjectArray;
+ activity: TObject;
+ featured: boolean;
+ isFree: boolean;
+ isPaid: boolean;
+ isBundle: boolean;
+ isCustomPlugin: boolean;
+ shop: IPluginShopDetails;
+ bundle: TObject; // has nested plugins array
+ specialOffer: string;
+ versions: TObjectArray;
+ isDownloadable: boolean;
+ changelog: TObject;
+ consumer: TObject;
+ isInstalled: boolean;
+ isActivated: boolean;
+ isInvalid: boolean;
+ canBeUpdated: boolean;
+ canBePurchased: boolean;
+ hasExceededLicense: boolean;
+ isMissingLicense: boolean;
+ missingRequirements: TObjectArray;
+ isEligibleForFreeTrial: boolean;
+ priceFrom: IPluginShopVariation;
+ coverImage: string;
+ numDownloadsPretty: TNumberOrString;
+}
+
+declare global {
+ interface Window {
+ iFrameResize: typeof iframeResizer;
+ }
+}