From 077097fac282dfbdba605f9e4283fd39957911c2 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 5 Oct 2023 14:25:40 -0400 Subject: [PATCH 01/56] Creates Price and Credit icon components --- src/common/icons/Credit.svelte | 36 +++++++++++++++++++ src/premium-credits/Price.svelte | 25 +++++++++++++ .../stories/Price.stories.svelte | 22 ++++++++++++ src/style/variables.css | 2 ++ 4 files changed, 85 insertions(+) create mode 100644 src/common/icons/Credit.svelte create mode 100644 src/premium-credits/Price.svelte create mode 100644 src/premium-credits/stories/Price.stories.svelte diff --git a/src/common/icons/Credit.svelte b/src/common/icons/Credit.svelte new file mode 100644 index 000000000..1ffde73d4 --- /dev/null +++ b/src/common/icons/Credit.svelte @@ -0,0 +1,36 @@ + + + + + + {title} + + diff --git a/src/premium-credits/Price.svelte b/src/premium-credits/Price.svelte new file mode 100644 index 000000000..a094001fd --- /dev/null +++ b/src/premium-credits/Price.svelte @@ -0,0 +1,25 @@ + + + + + + + {value?.toLocaleString()} + diff --git a/src/premium-credits/stories/Price.stories.svelte b/src/premium-credits/stories/Price.stories.svelte new file mode 100644 index 000000000..8f1ac0ec1 --- /dev/null +++ b/src/premium-credits/stories/Price.stories.svelte @@ -0,0 +1,22 @@ + + + + + + + diff --git a/src/style/variables.css b/src/style/variables.css index 6c8744c17..1f6ef7048 100644 --- a/src/style/variables.css +++ b/src/style/variables.css @@ -3,6 +3,7 @@ --primary: #4294f0; --primary-faded: rgba(66, 148, 240, 0.13); --sidebar: #edeeef; + --white: #ffffff; --black: #000000; --gray: rgba(0, 0, 0, 0.53); --darkgray: rgba(0, 0, 0, 0.8); @@ -23,6 +24,7 @@ --actionPane: #fffdea; --highlight-orange: #ff785c; --inputBg: #ffffff; + --premium: #24CC99; /* Viewer */ --viewerPaneColor: #f3f3f3; From 2f05ab32abb6d9ecc59d6fae097f919d3850b356 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 5 Oct 2023 14:48:37 -0400 Subject: [PATCH 02/56] Creates UpgradePrompt for premium add-ons --- src/common/Button.svelte | 7 +++ src/langs/json/en.json | 18 ++++--- src/premium-credits/UpgradePrompt.svelte | 49 +++++++++++++++++++ .../stories/UpgradePrompt.stories.svelte | 25 ++++++++++ 4 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 src/premium-credits/UpgradePrompt.svelte create mode 100644 src/premium-credits/stories/UpgradePrompt.stories.svelte diff --git a/src/common/Button.svelte b/src/common/Button.svelte index 2e9edd2f1..1d9cb7810 100644 --- a/src/common/Button.svelte +++ b/src/common/Button.svelte @@ -8,6 +8,7 @@ export let small = false; export let secondary = false; export let tertiary = false; + export let premium = false; export let nondescript = false; export let action = false; export let caution = false; @@ -71,6 +72,10 @@ background: var(--tertiary, #0c8a01); } + .premium { + background: var(--premium, #24cc99); + } + .danger { background: var(--caution, #f04c42); } @@ -138,6 +143,7 @@ on:click class:secondary class:tertiary + class:premium class:danger class:small class:caution @@ -157,6 +163,7 @@ on:click class:secondary class:tertiary + class:premium class:danger class:small class:caution diff --git a/src/langs/json/en.json b/src/langs/json/en.json index 8210a6fb5..6ab3e75c5 100644 --- a/src/langs/json/en.json +++ b/src/langs/json/en.json @@ -729,7 +729,11 @@ "scheduleSuccess": "Add-on is now scheduled", "runSuccess": "Add-on is now running", "selectionHelp": "From the main list, select individual documents or run a search for the documents you want, for example “+project:mueller-docs-200005”.", - "selectionLearnMore": "Learn more about how Add-Ons work" + "selectionLearnMore": "Learn more about how Add-Ons work", + "premiumUpagrade": { + "message": "This Premium Add-On uses AI to perform advanced analysis. Upgrade to a Professional account to utilize this and other powerful Add-Ons.", + "callToAction": "Start Free Trial" + } }, "addonBrowserDialog": { "title": "Browse Add-Ons", @@ -783,11 +787,11 @@ "disable": "Disable upload via email" }, "anonymous": { - "title": "Welcome to DocumentCloud, an open document archive from MuckRock!", - "p1": "This site helps organize, analyze and host millions of records contributed by verified newsrooms, research organizations and other groups that help inform the public through the user of primary source materials. Using the search bar above, you can browse through {n} publicly published documents, with thousands more added ever day.", - "p2": "If you're part of a newsroom, academic organization, or other public-interest organization that vets and publishes materials in the public interest, you can register here and request verification to upload materials, or learn more about DocumentCloud and it's powerful suite of hosting, analysis and publication tools.", - "p3": "Want more fascinating documents, open data and original reporting to your inbox? Subscribe to MuckRock's newsletter:", - "p4": "DocumentCloud is part of a suite of transparency tools from the MuckRock Foundation, a 501c3 registered non-profit. This archive is open to the public and advertisement free thanks to support from readers like you — you can learn more about our work or make a donation.", - "subscribe": "Subscribe" + "title": "Welcome to DocumentCloud, an open document archive from MuckRock!", + "p1": "This site helps organize, analyze and host millions of records contributed by verified newsrooms, research organizations and other groups that help inform the public through the user of primary source materials. Using the search bar above, you can browse through {n} publicly published documents, with thousands more added ever day.", + "p2": "If you're part of a newsroom, academic organization, or other public-interest organization that vets and publishes materials in the public interest, you can register here and request verification to upload materials, or learn more about DocumentCloud and it's powerful suite of hosting, analysis and publication tools.", + "p3": "Want more fascinating documents, open data and original reporting to your inbox? Subscribe to MuckRock's newsletter:", + "p4": "DocumentCloud is part of a suite of transparency tools from the MuckRock Foundation, a 501c3 registered non-profit. This archive is open to the public and advertisement free thanks to support from readers like you — you can learn more about our work or make a donation.", + "subscribe": "Subscribe" } } diff --git a/src/premium-credits/UpgradePrompt.svelte b/src/premium-credits/UpgradePrompt.svelte new file mode 100644 index 000000000..dcebba411 --- /dev/null +++ b/src/premium-credits/UpgradePrompt.svelte @@ -0,0 +1,49 @@ + + + + +
+
+ +

{message}

+
+
+
+
diff --git a/src/premium-credits/stories/UpgradePrompt.stories.svelte b/src/premium-credits/stories/UpgradePrompt.stories.svelte new file mode 100644 index 000000000..dca08539f --- /dev/null +++ b/src/premium-credits/stories/UpgradePrompt.stories.svelte @@ -0,0 +1,25 @@ + + + + + + + From 31b1629e4a3ed8c7173b71f73cd819f85af31435 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 5 Oct 2023 15:07:25 -0400 Subject: [PATCH 03/56] Add Premium filter to Add-On Browser --- src/addons/browser/Browser.svelte | 10 ++++++++++ src/addons/browser/Filters.svelte | 17 +++++++++++++++++ src/common/icons/Credit.svelte | 6 ++++-- src/langs/json/en.json | 3 ++- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/addons/browser/Browser.svelte b/src/addons/browser/Browser.svelte index c8c2cc9bf..3056183a9 100644 --- a/src/addons/browser/Browser.svelte +++ b/src/addons/browser/Browser.svelte @@ -11,6 +11,7 @@ import Filters, { filter, FILTERS, CATEGORIES } from "./Filters.svelte"; import Pin from "../../common/icons/Pin.svelte"; import Star from "../../common/icons/Star.svelte"; + import Credit from "../../common/icons/Credit.svelte"; export let visible = false; export let per_page = 10; @@ -201,6 +202,10 @@ fill: orange; } } + .premium.tip { + background-color: hsl(161, 69%, 91%); + border-color: var(--premium, #24cc99); + } @@ -225,6 +230,11 @@

{$_("addonBrowserDialog.featuredTip")}

+ {:else if $filter === "premium"} + {/if} diff --git a/src/addons/browser/Filters.svelte b/src/addons/browser/Filters.svelte index 7d81fb107..343fcc3fa 100644 --- a/src/addons/browser/Filters.svelte +++ b/src/addons/browser/Filters.svelte @@ -6,6 +6,7 @@ ["all", "All"], ["active", "Pinned"], ["featured", "Featured"], + ["premium", "Premium"], ]; export const CATEGORIES = [ @@ -26,6 +27,7 @@ import Pin from "../../common/icons/Pin.svelte"; import Star from "../../common/icons/Star.svelte"; import Infinity from "svelte-octicons/lib/Infinity16.svelte"; + import Credit from "../../common/icons/Credit.svelte";
    @@ -80,6 +86,17 @@ +
  • + + + + +

{$_("addonBrowserDialog.categories")}

diff --git a/src/common/icons/Credit.svelte b/src/common/icons/Credit.svelte index 1ffde73d4..77d1bef38 100644 --- a/src/common/icons/Credit.svelte +++ b/src/common/icons/Credit.svelte @@ -3,6 +3,7 @@ export let size = 1; export let title = "Credit"; export let badge = false; + export let color = "#24CC99"; diff --git a/src/addons/browser/Filters.svelte b/src/addons/browser/Filters.svelte index 343fcc3fa..fa74f660d 100644 --- a/src/addons/browser/Filters.svelte +++ b/src/addons/browser/Filters.svelte @@ -89,12 +89,7 @@
  • - +
  • diff --git a/src/common/icons/Credit.svelte b/src/common/icons/Credit.svelte index 77d1bef38..04776fd19 100644 --- a/src/common/icons/Credit.svelte +++ b/src/common/icons/Credit.svelte @@ -13,13 +13,6 @@ .icon path { fill: var(--credit-color, --premium); } - .badge { - background: var(--credit-color, --premium); - border-radius: 50%; - } - .badge path { - fill: var(--white); - } {title} - + {#if badge} + + {:else} + + {/if} From 33b1e8eebfd5b00f703fc69d8bf011bd773941f8 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 5 Oct 2023 16:58:13 -0400 Subject: [PATCH 05/56] Adds Premium badge to premium addon list items --- src/addons/browser/AddOnListItem.svelte | 22 ++++++++++-- .../stories/AddOnListItem.stories.svelte | 1 + src/addons/types.ts | 1 + src/common/Badge.svelte | 35 +++++++++++++++++++ src/common/stories/Badge.demo.svelte | 12 +++++++ src/common/stories/Badge.stories.svelte | 28 +++++++++++++++ 6 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/common/Badge.svelte create mode 100644 src/common/stories/Badge.demo.svelte create mode 100644 src/common/stories/Badge.stories.svelte diff --git a/src/addons/browser/AddOnListItem.svelte b/src/addons/browser/AddOnListItem.svelte index 7c7afd567..57da12f26 100644 --- a/src/addons/browser/AddOnListItem.svelte +++ b/src/addons/browser/AddOnListItem.svelte @@ -4,6 +4,8 @@ import AddOnPin from "../AddOnPin.svelte"; import AddOnPopularity from "../Popularity.svelte"; import type { AddOnListItem } from "../types.js"; + import Credit from "../../common/icons/Credit.svelte"; + import Badge from "../../common/Badge.svelte"; export let addon: AddOnListItem; @@ -26,13 +28,18 @@ text-align: left; } - .top-row { + .row { display: flex; align-items: flex-end; gap: 0.5rem; margin: 0.5rem; } + .badge { + margin-bottom: -0.25em; + font-size: 0.8em; + } + .metadata { display: flex; align-items: flex-end; @@ -79,7 +86,7 @@
    -
    +
    @@ -100,6 +107,17 @@ {#if addon.usage} {/if} + {#if addon.premium} + + + + {/if}
    {#if description} diff --git a/src/addons/browser/stories/AddOnListItem.stories.svelte b/src/addons/browser/stories/AddOnListItem.stories.svelte index da17ea7ac..f807b5b75 100644 --- a/src/addons/browser/stories/AddOnListItem.stories.svelte +++ b/src/addons/browser/stories/AddOnListItem.stories.svelte @@ -43,3 +43,4 @@ }, }} /> + diff --git a/src/addons/types.ts b/src/addons/types.ts index 511971f72..7a8d3905c 100644 --- a/src/addons/types.ts +++ b/src/addons/types.ts @@ -16,5 +16,6 @@ export interface AddOnListItem { documents: string[]; active: boolean; featured: boolean; + premium?: boolean; default: boolean; } diff --git a/src/common/Badge.svelte b/src/common/Badge.svelte new file mode 100644 index 000000000..ed64c81a0 --- /dev/null +++ b/src/common/Badge.svelte @@ -0,0 +1,35 @@ + + + + +
    + {#if $$slots.icon}{/if} + {label} +
    diff --git a/src/common/stories/Badge.demo.svelte b/src/common/stories/Badge.demo.svelte new file mode 100644 index 000000000..69f8136cc --- /dev/null +++ b/src/common/stories/Badge.demo.svelte @@ -0,0 +1,12 @@ + + + + + diff --git a/src/common/stories/Badge.stories.svelte b/src/common/stories/Badge.stories.svelte new file mode 100644 index 000000000..19c55e58c --- /dev/null +++ b/src/common/stories/Badge.stories.svelte @@ -0,0 +1,28 @@ + + + + + + + + From 4f6aa974ff59066339458252b7fad7910ef21ba8 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Fri, 6 Oct 2023 11:03:05 -0400 Subject: [PATCH 06/56] Adds Premium Add-On to Dispatch stories --- .../dispatch/stories/Dispatch.stories.svelte | 2 + src/addons/fixtures/addons.json | 326 ++++++++++++++++++ 2 files changed, 328 insertions(+) diff --git a/src/addons/dispatch/stories/Dispatch.stories.svelte b/src/addons/dispatch/stories/Dispatch.stories.svelte index a78b02560..a5b8f202b 100644 --- a/src/addons/dispatch/stories/Dispatch.stories.svelte +++ b/src/addons/dispatch/stories/Dispatch.stories.svelte @@ -97,3 +97,5 @@ + + diff --git a/src/addons/fixtures/addons.json b/src/addons/fixtures/addons.json index 9aded61c5..1ebb3b285 100644 --- a/src/addons/fixtures/addons.json +++ b/src/addons/fixtures/addons.json @@ -237,5 +237,331 @@ "active": false, "default": false, "featured": false + }, + { + "id": 455, + "user": 100000, + "organization": 10001, + "access": "public", + "name": "Translate Documents", + "repository": "MuckRock/google-translate-addon", + "parameters": { + "type": "object", + "title": "Translate Documents", + "required": ["access_level", "output_lang", "input_lang"], + "documents": ["selected"], + "categories": ["ai", "premium", "file"], + "properties": { + "dry_run": { + "type": "boolean", + "title": "Dry Run", + "description": "Select this to calculate the cost of running this translation, it won't translate the text if selected." + }, + "input_lang": { + "enum": [ + "af", + "sq", + "am", + "ar", + "hy", + "as", + "ay", + "az", + "bm", + "eu", + "be", + "bn", + "bho", + "bs", + "bg", + "ca", + "ceb", + "zh-CN", + "zh-TW", + "co", + "hr", + "cs", + "da", + "dv", + "doi", + "nl", + "en", + "eo", + "et", + "ee", + "fil", + "fi", + "fr", + "fy", + "gl", + "ka", + "de", + "el", + "gn", + "gu", + "ht", + "ha", + "haw", + "he", + "hi", + "hmn", + "hu", + "is", + "ig", + "ilo", + "id", + "ga", + "it", + "ja", + "jv", + "kn", + "kk", + "km", + "rw", + "gom", + "ko", + "kri", + "ku", + "ckb", + "ky", + "lo", + "la", + "lv", + "ln", + "lt", + "lg", + "lb", + "mk", + "mai", + "mg", + "ms", + "ml", + "mt", + "mi", + "mr", + "mni-Mtei", + "lus", + "mn", + "my", + "ne", + false, + "ny", + "or", + "om", + "ps", + "fa", + "pl", + "pt", + "pa", + "qu", + "ro", + "ru", + "sm", + "sa", + "gd", + "nso", + "sr", + "st", + "sn", + "sd", + "si", + "sk", + "sl", + "so", + "es", + "su", + "sw", + "sv", + "tl", + "tg", + "ta", + "tt", + "te", + "th", + "ti", + "ts", + "tr", + "tk", + "ak", + "uk", + "ur", + "ug", + "uz", + "vi", + "cy", + "xh", + "yi", + "yo", + "zu" + ], + "type": "string", + "title": "Input language code", + "default": "en" + }, + "project_id": { + "type": "integer", + "title": "Project ID", + "description": "(Optional) Project ID of project you want to upload the translations to." + }, + "output_lang": { + "enum": [ + "af", + "sq", + "am", + "ar", + "hy", + "as", + "ay", + "az", + "bm", + "eu", + "be", + "bn", + "bho", + "bs", + "bg", + "ca", + "ceb", + "zh-CN", + "zh-TW", + "co", + "hr", + "cs", + "da", + "dv", + "doi", + "nl", + "en", + "eo", + "et", + "ee", + "fil", + "fi", + "fr", + "fy", + "gl", + "ka", + "de", + "el", + "gn", + "gu", + "ht", + "ha", + "haw", + "he", + "hi", + "hmn", + "hu", + "is", + "ig", + "ilo", + "id", + "ga", + "it", + "ja", + "jv", + "kn", + "kk", + "km", + "rw", + "gom", + "ko", + "kri", + "ku", + "ckb", + "ky", + "lo", + "la", + "lv", + "ln", + "lt", + "lg", + "lb", + "mk", + "mai", + "mg", + "ms", + "ml", + "mt", + "mi", + "mr", + "mni-Mtei", + "lus", + "mn", + "my", + "ne", + false, + "ny", + "or", + "om", + "ps", + "fa", + "pl", + "pt", + "pa", + "qu", + "ro", + "ru", + "sm", + "sa", + "gd", + "nso", + "sr", + "st", + "sn", + "sd", + "si", + "sk", + "sl", + "so", + "es", + "su", + "sw", + "sv", + "tl", + "tg", + "ta", + "tt", + "te", + "th", + "ti", + "ts", + "tr", + "tk", + "ak", + "uk", + "ur", + "ug", + "uz", + "vi", + "cy", + "xh", + "yi", + "yo", + "zu" + ], + "type": "string", + "title": "Output language code", + "default": "es" + }, + "access_level": { + "enum": ["public", "private", "organization"], + "type": "string", + "title": "Access level (public, private, organization) of translations", + "default": "public" + } + }, + "description": "

    This Add-On allows you to translate documents using the Google Translate service. Supply a two character ISO 639-1 code for the input and output languages and you will receive your translation in a download as well as it will be uploaded to DocumentCloud. See https://cloud.google.com/translate/docs/languages for supported languages.

    " + }, + "created_at": "2023-07-19T19:59:18.086713Z", + "updated_at": "2023-07-19T19:59:18.088614Z", + "active": false, + "default": false, + "featured": false, + "premium": true, + "premium_cost": { + "amount": 75, + "unit": "character" + } } ] From 9e4ffb9405ce646e31471054994463c986371958 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Fri, 6 Oct 2023 11:03:13 -0400 Subject: [PATCH 07/56] Adds Premium badge to Dispatch header --- src/addons/dispatch/Header.svelte | 15 +++++++++++++-- src/common/icons/Credit.svelte | 2 -- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/addons/dispatch/Header.svelte b/src/addons/dispatch/Header.svelte index 80f682ae5..92e3e0a69 100644 --- a/src/addons/dispatch/Header.svelte +++ b/src/addons/dispatch/Header.svelte @@ -9,6 +9,8 @@ import { pushToast } from "../../common/Toast.svelte"; import AddOnPin from "../AddOnPin.svelte"; + import Badge from "../../common/Badge.svelte"; + import Credit from "../../common/icons/Credit.svelte"; export let addon: AddOnListItem; @@ -54,14 +56,15 @@ .name { flex: 1 1 100%; display: flex; - align-items: flex-start; - gap: 1rem; + align-items: center; + gap: 1em; } .pin { flex: 0 1 auto; } .name h2 { margin: 0; + flex: 1 1 auto; } .metadata { display: flex; @@ -116,6 +119,14 @@

    {addon.name}

    + {#if addon.premium} + + {/if}
    -
    - - -
    -
    {@html addon?.parameters?.description}
    diff --git a/src/addons/fixtures/addons.json b/src/addons/fixtures/addons.json index 27d567c44..ba7304a40 100644 --- a/src/addons/fixtures/addons.json +++ b/src/addons/fixtures/addons.json @@ -563,6 +563,32 @@ "default": false, "featured": false }, + { + "id": 46, + "user": null, + "organization": null, + "access": "public", + "name": "Azure Document Intelligence OCR", + "repository": "MuckRock/documentcloud-azure-document-intelligence-ocr-addon", + "parameters": { + "cost": { + "unit": "page", + "amount": 1 + }, + "type": "object", + "title": "Azure Document Intelligence OCR", + "documents": ["selected"], + "categories": ["extraction", "premium"], + "properties": {}, + "description": "

    This Add-On uses Azure’s Document Intelligence API to OCR documents. The document(s) must be public to be processed. This Add-On uses 1 AI Credit per page.

    ", + "instructions": "" + }, + "created_at": "2023-09-18T17:09:58.097621Z", + "updated_at": "2023-11-15T16:04:34.442286Z", + "active": false, + "default": false, + "featured": false + }, { "id": 360, "user": 100000, From e5d298c7f52f2506c58fd62d9001b1109819e186 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Fri, 17 Nov 2023 16:56:42 -0500 Subject: [PATCH 45/56] Handles plan upgrade action and updates language --- src/langs/json/en.json | 4 ++-- src/manager/orgsAndUsers.js | 6 ++++-- src/pages/app/AccountNavigation/PremiumMenu.svelte | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/langs/json/en.json b/src/langs/json/en.json index c357f9fd8..7bbd05bc2 100644 --- a/src/langs/json/en.json +++ b/src/langs/json/en.json @@ -375,7 +375,7 @@ "docs": "Learn more about DocumentCloud Premium", "addons": "Explore premium add-ons", "orgs": "Join or start an organization", - "startTrial": "Start Free Trial" + "cta": "Upgrade Plan" }, "org": { "changeOrg": "Change organization", @@ -763,7 +763,7 @@ "selectionLearnMore": "Learn more about how Add-Ons work", "premiumUpgrade": { "message": "This Premium Add-On uses AI to perform advanced analysis. Upgrade to a Professional account to utilize this and other powerful Add-Ons.", - "callToAction": "Start Free Trial" + "callToAction": "Upgrade Plan" } }, "addonBrowserDialog": { diff --git a/src/manager/orgsAndUsers.js b/src/manager/orgsAndUsers.js index 810f9473c..48f2d4e12 100644 --- a/src/manager/orgsAndUsers.js +++ b/src/manager/orgsAndUsers.js @@ -11,6 +11,7 @@ import { getUsers, getOrganization, } from "../api/orgAndUser.js"; +import { SQUARELET_URL } from "../api/auth.js"; import { projects, initProjects } from "./projects.js"; import { userUrl, allDocumentsUrl } from "../search/search.js"; import { layout } from "./layout.js"; @@ -229,9 +230,10 @@ export function getCreditBalance(org) { return org.monthly_credits + org.purchased_credits; } -// TODO: Handle flow to upgrade user to Pro account export async function triggerPremiumUpgradeFlow() { - alert("Upgrade to Premium!"); + // Redirect the user to their Squarelet account settings + const url = SQUARELET_URL + `/users/~payment/`; + window?.open(url); } // TODO: Handle flow for purchasing premium credits diff --git a/src/pages/app/AccountNavigation/PremiumMenu.svelte b/src/pages/app/AccountNavigation/PremiumMenu.svelte index 485420e6f..4a57a889b 100644 --- a/src/pages/app/AccountNavigation/PremiumMenu.svelte +++ b/src/pages/app/AccountNavigation/PremiumMenu.svelte @@ -104,7 +104,7 @@ {$_("authSection.premiumUpgrade.description")}

    Your credit balance
    +
    diff --git a/src/manager/orgsAndUsers.js b/src/manager/orgsAndUsers.js index 48f2d4e12..a9b10f7d1 100644 --- a/src/manager/orgsAndUsers.js +++ b/src/manager/orgsAndUsers.js @@ -236,7 +236,7 @@ export async function triggerPremiumUpgradeFlow() { window?.open(url); } -// TODO: Handle flow for purchasing premium credits +// TODO: Handle flow for purchasing premium credits (#342) export async function triggerCreditPurchaseFlow() { alert("Purchase Credits!"); } diff --git a/src/pages/app/AccountNavigation/OrgMenu.svelte b/src/pages/app/AccountNavigation/OrgMenu.svelte index 85b32d51f..6fb7aa688 100644 --- a/src/pages/app/AccountNavigation/OrgMenu.svelte +++ b/src/pages/app/AccountNavigation/OrgMenu.svelte @@ -108,6 +108,7 @@ value={activeOrg.monthly_credits} max={activeOrg.monthly_credit_allowance} /> + {#await listOrgsPromise then orgOptions} diff --git a/src/pages/app/AccountNavigation/PremiumMenu.svelte b/src/pages/app/AccountNavigation/PremiumMenu.svelte index 4a57a889b..9db21ec26 100644 --- a/src/pages/app/AccountNavigation/PremiumMenu.svelte +++ b/src/pages/app/AccountNavigation/PremiumMenu.svelte @@ -84,6 +84,7 @@ value={monthly_credits} max={monthly_credit_allowance} /> + {:else} From 8d19d75be824974fd64f76ce25d4c77d9ebb11b4 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Fri, 17 Nov 2023 17:16:53 -0500 Subject: [PATCH 47/56] Removes OrgUsers from application Sidebar --- src/pages/app/sidebar/OrgUsers.svelte | 76 --------------------------- src/pages/app/sidebar/Sidebar.svelte | 2 - 2 files changed, 78 deletions(-) delete mode 100644 src/pages/app/sidebar/OrgUsers.svelte diff --git a/src/pages/app/sidebar/OrgUsers.svelte b/src/pages/app/sidebar/OrgUsers.svelte deleted file mode 100644 index d313e1aa1..000000000 --- a/src/pages/app/sidebar/OrgUsers.svelte +++ /dev/null @@ -1,76 +0,0 @@ - - - - -
    - {#if $orgsAndUsers.me !== null && !$orgsAndUsers.me.organization.individual} -
    - -

    - {$_("organizations.sameOrgUsers")}: {$orgsAndUsers.me.organization.name} -

    -
    - -
      - {#each $orgsAndUsers.sameOrgUsers as user} -
    • - - {user.name} - {#if user.admin_organizations.includes($orgsAndUsers.me.organization.id)} - (Admin) - {/if} - -
    • - {/each} -
    -
    - {/if} -
    diff --git a/src/pages/app/sidebar/Sidebar.svelte b/src/pages/app/sidebar/Sidebar.svelte index 41190400d..981382a04 100644 --- a/src/pages/app/sidebar/Sidebar.svelte +++ b/src/pages/app/sidebar/Sidebar.svelte @@ -7,7 +7,6 @@ import ProjectFilters from "./ProjectFilters.svelte"; import Projects from "./Projects.svelte"; - import OrgUsers from "./OrgUsers.svelte"; import AddonSidebar from "../../../addons/sidebar/Sidebar.svelte"; @@ -73,7 +72,6 @@ {#if $orgsAndUsers.me !== null} - {/if} From 9c93761c28f0d8b3430ee5dbb8f04724f8babd89 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Tue, 21 Nov 2023 16:39:53 -0500 Subject: [PATCH 48/56] Updates AddOn Dispatch Premium component to account for free Org accounts --- src/addons/dispatch/Dispatch.svelte | 2 +- src/addons/dispatch/Premium.svelte | 26 ++++-- .../dispatch/stories/Premium.stories.svelte | 91 +++++++++++++++++-- src/langs/json/en.json | 5 +- src/manager/orgsAndUsers.js | 24 ++++- src/pages/app/AccountNavigation/types.ts | 1 + src/premium-credits/UpgradePrompt.svelte | 10 +- .../stories/UpgradePrompt.stories.svelte | 17 ++-- 8 files changed, 144 insertions(+), 32 deletions(-) diff --git a/src/addons/dispatch/Dispatch.svelte b/src/addons/dispatch/Dispatch.svelte index 0a9e32e73..685e41e8c 100644 --- a/src/addons/dispatch/Dispatch.svelte +++ b/src/addons/dispatch/Dispatch.svelte @@ -307,7 +307,7 @@ documents={new Set(addon.parameters.documents)} /> - +
    diff --git a/src/addons/dispatch/Premium.svelte b/src/addons/dispatch/Premium.svelte index bc99133f0..18a23fe7a 100644 --- a/src/addons/dispatch/Premium.svelte +++ b/src/addons/dispatch/Premium.svelte @@ -7,18 +7,24 @@ import Price from "../../premium-credits/Price.svelte"; import UpgradePrompt from "../../premium-credits/UpgradePrompt.svelte"; import { + isPremiumOrg, + getCreditBalance, triggerCreditPurchaseFlow, triggerPremiumUpgradeFlow, + isOrgAdmin, } from "../../manager/orgsAndUsers"; + import { User } from "../../pages/app/AccountNavigation/types"; export let addon: AddOnListItem; - export let isPremiumUser: boolean; - export let creditBalance: number; + export let user: User; let spendingLimitEnabled = false; let spendingLimit = 0; + $: creditBalance = getCreditBalance(user.organization); + $: isIndividualOrg = + typeof user.organization !== "string" && user.organization.individual; $: isPremium = addon?.parameters?.categories?.includes("premium") ?? false; const { amount, unit } = addon?.parameters?.cost ?? {}; $: prettyCost = amount && unit ? handlePlural(amount, unit) : null; @@ -138,7 +144,7 @@ {#if isPremium} - {#if isPremiumUser} + {#if isPremiumOrg(user.organization)}
    {$_("addonDispatchDialog.premium")}
    @@ -192,11 +198,19 @@
    - {:else} + {:else if isOrgAdmin(user)} triggerPremiumUpgradeFlow(user.organization)} + /> + {:else} + {/if} {/if} diff --git a/src/addons/dispatch/stories/Premium.stories.svelte b/src/addons/dispatch/stories/Premium.stories.svelte index 7337eae1e..8a06e455a 100644 --- a/src/addons/dispatch/stories/Premium.stories.svelte +++ b/src/addons/dispatch/stories/Premium.stories.svelte @@ -4,6 +4,46 @@ import Premium from "../Premium.svelte"; import * as addons from "../../fixtures/addons.json"; + + const individualOrg = { + id: 4, + avatar_url: + "https://cdn.muckrock.com/media/account_images/allan-headshot-2016.jpg", + individual: true, + name: "lasser.allan", + slug: "lasserallan", + monthly_credits: 2500, + purchased_credits: 3000, + credit_reset_date: "2023-11-28", + monthly_credit_allowance: 2500, + plan: "Professional", + }; + + const groupOrg = { + id: 1, + avatar_url: + "https://squarelet-staging.s3.amazonaws.com/media/org_avatars/logo_uEHCMva.png", + individual: false, + name: "MuckRock", + slug: "muckrock", + monthly_credits: 5000, + purchased_credits: 0, + credit_reset_date: "2023-11-28", + monthly_credit_allowance: 5000, + plan: "Organization", + }; + + const user = { + id: 4, + avatar_url: + "https://cdn.muckrock.com/media/account_images/allan-headshot-2016.jpg", + name: "Allan Lasser", + organization: groupOrg, + organizations: [1, 4], + admin_organizations: [4], + username: "lasser.allan", + verified_journalist: true, + }; + + + + diff --git a/src/langs/json/en.json b/src/langs/json/en.json index 7bbd05bc2..5a446bf23 100644 --- a/src/langs/json/en.json +++ b/src/langs/json/en.json @@ -762,8 +762,9 @@ "selectionHelp": "From the main list, select individual documents or run a search for the documents you want, for example “+project:mueller-docs-200005”.", "selectionLearnMore": "Learn more about how Add-Ons work", "premiumUpgrade": { - "message": "This Premium Add-On uses AI to perform advanced analysis. Upgrade to a Professional account to utilize this and other powerful Add-Ons.", - "callToAction": "Upgrade Plan" + "message": "This Premium Add-On uses AI to perform advanced analysis. Upgrade to a {plan} account to utilize this and other powerful Add-Ons.", + "callToAction": "Upgrade Plan", + "memberMessage": "This Premium Add-On uses AI to perform advanced analysis. Contact your organization admin about upgrading your plan." } }, "addonBrowserDialog": { diff --git a/src/manager/orgsAndUsers.js b/src/manager/orgsAndUsers.js index a9b10f7d1..0bce200fb 100644 --- a/src/manager/orgsAndUsers.js +++ b/src/manager/orgsAndUsers.js @@ -220,9 +220,17 @@ export async function inMyOrg(orgId, myId) { return [...adminUsers, ...regularUsers].filter((u) => u.id !== myId); } +export function isOrgAdmin(user) { + const id = + typeof user.organization === "string" + ? user.organization + : user.organization.id; + return user.admin_organizations.includes(id); +} + export function isPremiumOrg(org) { - if (!org) return null; - return !org.individual || org.plan === "Professional"; + if (!org || !org.plan) return null; + return ["Professional", "Organization"].includes(org.plan); } export function getCreditBalance(org) { @@ -230,9 +238,15 @@ export function getCreditBalance(org) { return org.monthly_credits + org.purchased_credits; } -export async function triggerPremiumUpgradeFlow() { - // Redirect the user to their Squarelet account settings - const url = SQUARELET_URL + `/users/~payment/`; +export async function triggerPremiumUpgradeFlow(org) { + let url; + if (org.individual) { + // Redirect the user to their Squarelet account settings + url = SQUARELET_URL + `/users/~payment/`; + } else { + // Redirect the user to the Squarelet organization settings + url = SQUARELET_URL + `/organizations/${org.slug}/payment/`; + } window?.open(url); } diff --git a/src/pages/app/AccountNavigation/types.ts b/src/pages/app/AccountNavigation/types.ts index 5bc4554fa..99ea5ffe7 100644 --- a/src/pages/app/AccountNavigation/types.ts +++ b/src/pages/app/AccountNavigation/types.ts @@ -10,6 +10,7 @@ interface PremiumOrgFields { export interface Org extends Partial { id: string; name: string; + slug: string; avatar_url: string; individual: boolean; plan: "Free" | "Professional" | "Organization"; diff --git a/src/premium-credits/UpgradePrompt.svelte b/src/premium-credits/UpgradePrompt.svelte index 8d821fc9d..70a88557b 100644 --- a/src/premium-credits/UpgradePrompt.svelte +++ b/src/premium-credits/UpgradePrompt.svelte @@ -4,7 +4,7 @@ import Button from "../common/Button.svelte"; export let message: string; - export let callToAction: string; + export let callToAction: string | null = null; {#if isPremium} - {#if isPremiumOrg(user.organization)} + {#if isPremiumOrg(user?.organization)}
    {$_("addonDispatchDialog.premium")}
    @@ -206,7 +208,7 @@ }, })} callToAction={$_("addonDispatchDialog.premiumUpgrade.callToAction")} - on:click={() => triggerPremiumUpgradeFlow(user.organization)} + on:click={() => triggerPremiumUpgradeFlow(user?.organization)} /> {:else} Date: Wed, 22 Nov 2023 16:11:46 -0500 Subject: [PATCH 51/56] Fixes Playwright tests --- src/pages/app/AccountNavigation/LanguageMenu.svelte | 5 +---- tests/anonymous/manager/app.spec.js | 10 +++++----- tests/anonymous/pages/home.spec.js | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/pages/app/AccountNavigation/LanguageMenu.svelte b/src/pages/app/AccountNavigation/LanguageMenu.svelte index 2154a28cc..ccb1bb3f3 100644 --- a/src/pages/app/AccountNavigation/LanguageMenu.svelte +++ b/src/pages/app/AccountNavigation/LanguageMenu.svelte @@ -32,10 +32,7 @@ {#if langs.length > 1} - +
    diff --git a/tests/anonymous/manager/app.spec.js b/tests/anonymous/manager/app.spec.js index 41703742b..5b338d954 100644 --- a/tests/anonymous/manager/app.spec.js +++ b/tests/anonymous/manager/app.spec.js @@ -29,16 +29,16 @@ test.describe("manager tests", () => { await page.goto("/app"); // help - await page.getByText("Help ▼").click(); + await page.getByText("Help", { exact: true }).dispatchEvent("click"); await expect(page.getByRole("button", { name: "FAQ" })).toBeVisible(); // close the menu - await page.locator(".shim").click(); + await page.locator(".overlay").click(); // language - await page.getByText("Language ▼").click(); - await expect(page.getByRole("button", { name: "English ✓" })).toBeVisible(); + await page.getByText("Language", { exact: true }).dispatchEvent("click"); + await expect(page.getByRole("button", { name: "Español" })).toBeVisible(); - await page.locator(".shim").click(); + await page.locator(".overlay").click(); }); }); diff --git a/tests/anonymous/pages/home.spec.js b/tests/anonymous/pages/home.spec.js index 699c9aecc..6bc3be552 100644 --- a/tests/anonymous/pages/home.spec.js +++ b/tests/anonymous/pages/home.spec.js @@ -11,7 +11,7 @@ test("basic homepage test", async ({ page }) => { await page.getByRole("banner").getByRole("link").first().click(); // and back - await page.getByRole("link", { name: "Home" }).click(); + await page.goBack(); await expect(page).toHaveTitle("Home | DocumentCloud"); }); From 7ca13f57f66a598f4a706c89de4fb8ce4456c94a Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Tue, 28 Nov 2023 15:14:36 -0500 Subject: [PATCH 52/56] Fixes org switching --- .../app/AccountNavigation/OrgMenu.svelte | 95 ++++++++++--------- .../app/AccountNavigation/OrgPicker.svelte | 4 +- 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/pages/app/AccountNavigation/OrgMenu.svelte b/src/pages/app/AccountNavigation/OrgMenu.svelte index 1f4c13c57..796880aaa 100644 --- a/src/pages/app/AccountNavigation/OrgMenu.svelte +++ b/src/pages/app/AccountNavigation/OrgMenu.svelte @@ -42,9 +42,9 @@ return orgs; } - async function changeOrg(id) { - changeActive(id); - getOrgPromise = getOrg(id); + async function changeOrg(org) { + changeActive(org); + getOrgPromise = getOrg(org.id); } let getOrgPromise = getOrg(org.id); @@ -70,6 +70,9 @@ color: var(--gray); margin: 0; } + .medium-width { + min-width: 20rem; + } {#await getOrgPromise} @@ -102,20 +105,21 @@ - {#if isPremiumOrg(activeOrg)} - - - - - {:else if isOrgAdmin(user)} - -
    -

    - {$_("authSection.premiumUpgrade.orgHeading")} -

    -

    - {$_("authSection.premiumUpgrade.orgDescription")} -

    -
    -
    - - {/if} - - {#await listOrgsPromise then orgOptions} - {#if orgOptions.length > 1} - + {/if} - {/await} + + {#await listOrgsPromise then orgOptions} + {#if orgOptions.length > 1} + + {/if} + {/await} +
    {/if} diff --git a/src/pages/app/AccountNavigation/OrgPicker.svelte b/src/pages/app/AccountNavigation/OrgPicker.svelte index a2fd72159..f5bf8843c 100644 --- a/src/pages/app/AccountNavigation/OrgPicker.svelte +++ b/src/pages/app/AccountNavigation/OrgPicker.svelte @@ -12,7 +12,7 @@ export let activeOrg: Org; export let loading = false; export let orgOptions: Org[] = []; - export let handleChange: (id: string) => void = () => {}; + export let handleChange: (org: Org) => void = () => {};