fix(deploy): prevent frontend patch failure and pre-bundle patches
All checks were successful
Deploy Plane Project Tracker / Deploy to cisystem (push) Successful in 55s
All checks were successful
Deploy Plane Project Tracker / Deploy to cisystem (push) Successful in 55s
- Check local disk first in patch_frontend.py to avoid remote HTTP retrieval - Use robust streaming with timeout and user-agent fallback instead of urlretrieve - Include pre-patched frontend JS files in deploy/patches - Add safe fallback in install.sh so optional patch generation does not fail deployment
This commit is contained in:
parent
e67f0cf15d
commit
b0e37091d4
5 changed files with 150 additions and 42 deletions
|
|
@ -79,7 +79,7 @@ docker compose run --rm migrator
|
|||
|
||||
echo "==> Generating frontend Enterprise patches..."
|
||||
if [ -f "${INSTALL_DIR}/patches/patch_frontend.py" ]; then
|
||||
python3 "${INSTALL_DIR}/patches/patch_frontend.py" "${INSTALL_DIR}/patches"
|
||||
python3 "${INSTALL_DIR}/patches/patch_frontend.py" "${INSTALL_DIR}/patches" || true
|
||||
fi
|
||||
|
||||
echo "==> [5/6] Starting Plane containers without touching other host services..."
|
||||
|
|
|
|||
1
deploy/patches/layout-C0Zt7niA.js
Normal file
1
deploy/patches/layout-C0Zt7niA.js
Normal file
File diff suppressed because one or more lines are too long
74
deploy/patches/page-BEQX0L4U.js
Normal file
74
deploy/patches/page-BEQX0L4U.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import{F as e,V as t,j as n}from"./chunk-KS7C4IRE-CMpS5gbI.js";import{t as r}from"./jsx-runtime-noaePsda.js";import{Br as i,J as a,Ti as o,c as s,dr as c,lr as l,vt as u,yt as d}from"./issue.service-CTC2rMYd.js";import{I as f,V as p,Vt as m,jn as h}from"./store-context-C83lXiWw.js";import{a as g}from"./dist-B3GnvwXu.js";import{a as _,i as v,n as y,r as b,t as x}from"./minus-BpQOnDpY.js";import{t as S}from"./item-icon-Cbeg99eb.js";import{t as C}from"./circle-check-ixOmpmsy.js";import{t as w}from"./message-square-C_OV-WER.js";import{t as T}from"./button-DrwsAH69-BTPMslkc.js";import"./button-BYpq06q8.js";import{t as E}from"./mobxreact.esm-CAsEUKPK.js";import{t as D}from"./page-title-CHr-UyVa.js";import{t as O}from"./use-workspace-Djnbfdo5.js";import{t as k}from"./user-NVe3rMCN.js";import{t as A}from"./boxed-control-item-CaQeQjDm.js";import{t as j}from"./heading-NrAC1MPz.js";import{t as M}from"./discount-info-t73U_blP.js";import{t as N}from"./breadcrumb-link-XZ0a1zCq.js";import{t as P}from"./not-authorized-view-BL2zvqmA.js";import{n as F,t as I}from"./page-header-5H3k3FUd.js";var L=r(),R=E(function(){let{t:e}=s(),t=i[`billing-and-plans`],n=S[`billing-and-plans`];return(0,L.jsx)(I,{leftItem:(0,L.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,L.jsx)(g,{children:(0,L.jsx)(g.Item,{component:(0,L.jsx)(N,{label:e(t.i18n_label),icon:(0,L.jsx)(n,{className:`size-4 text-tertiary`})})})})})})}),z=t(e(),1);function B({className:e}){return(0,L.jsx)(w,{className:p(e,`size-5 text-secondary`)})}function V({className:e}){return(0,L.jsx)(`span`,{className:p(`w-fit rounded-sm bg-accent-primary px-1.5 py-0.5 text-9 font-semibold whitespace-nowrap text-on-color`,e),children:`COMING SOON`})}var H=[`free`,`one`,`pro`,`business`,`enterprise`],U=[{id:`project-work-tracking`,title:`Project + work tracking`,features:[{title:`Projects`,description:`Add projects to house work items, cycles, and modules.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Work items`,description:`Add work via work items, set properties for tracking, and add to
|
||||
cycles or modules.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Comments`,description:`Respond to work items, @mention members, and brainstorm
|
||||
together without leaving Plane.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Cycles`,description:`Track work in timeboxes with differing frequency.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Modules`,description:`Group replicable work in modules with their own
|
||||
leads.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Intake`,description:`See suggestions and feedback from viewers and
|
||||
guests before you decide to add them to your
|
||||
project.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Estimates`,description:`Measure effort in points in a system that works for
|
||||
you.`,cloud:{free:`Basic`,one:`Basic`,pro:`Advanced`,business:`Advanced`,enterprise:`Advanced`}}]},{id:`project-work-management`,title:`Project + work management`,features:[{title:`Bulk Ops`,description:`Add several work items to cycles or modules, transfer
|
||||
them, or edit their properties.`,cloud:{free:!1,one:`Limited props`,pro:`All props`,business:(0,L.jsxs)(`span`,{className:`flex flex-col items-end gap-1 lg:items-center`,children:[(0,L.jsx)(V,{}),`Work item transfers and conversions`]}),enterprise:(0,L.jsxs)(`span`,{className:`flex flex-col items-end gap-1 lg:items-center`,children:[(0,L.jsx)(V,{}),`Work item transfers and conversions`]})}},{title:`Time Tracking + Worklogs`,description:`Track time per work item, see aggregated reports, and
|
||||
filter by need.`,cloud:{free:!1,one:`Basic`,pro:`Historical timesheets`,business:`Historical timesheets
|
||||
and approvals`,enterprise:`Historical timesheets
|
||||
and approvals`}},{title:`Active Cycles`,description:`See all running cycles across all projects, or soon, in
|
||||
a single project.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Work item Types`,description:`Create your own work item types with your own
|
||||
properties.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Custom Properties`,description:`Create your own properties and apply them to your
|
||||
workspace or project.`,cloud:{free:!1,one:!1,pro:`Project-level
|
||||
custom properties`,business:`Workspace-level
|
||||
properties and roll-ups`,enterprise:`Workspace-level
|
||||
properties and roll-ups`}},{title:`Dependencies in Gantt`,description:`Adjust timelines for dependent work items visually on
|
||||
our Gantt layout.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Work item Transfers`,description:`Move a work item from a project or a cycle to
|
||||
another.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Auto-transfer Cycle Work items`,description:`Transfer incomplete work items from a completed cycle
|
||||
to the next cycle or to the default project state. `,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Epics`,description:`Organize long-term work in epics that house work items,
|
||||
cycles, and modules.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Initiatives`,description:`Create initiatives to roll up several epics.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Checkpoints`,description:`Add markers to Projects, Epics and Initiatives to keep your
|
||||
team on track and report on progress.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Module Overview`,description:`Like Cycle Overviews, see relevant details and
|
||||
progress charts for each module.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Auto-assignment In Modules`,description:`Choose assignment rules for work items in a
|
||||
module including Linear, Round Robin, or Capacity.`,cloud:{free:!1,one:!1,pro:`Linear`,business:`Round-robin and Capacity`,enterprise:`Round-robin and Capacity`}},{title:`Public, Private, and Secret projects`,description:`Public projects are visible and accessible to
|
||||
everyone. Private ones are visible but need approval
|
||||
to join. Secret projects aren't visible or accessible.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`State Of Projects`,description:`See all projects laid across states that highlight
|
||||
those that need attention and those on track.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Pre-defined work item Templates`,description:`Choose from our available work item templates that
|
||||
customize work item types and properties for several
|
||||
use cases.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Teamspace Cycles`,description:`See multiple cycles in multiple projects at once.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Project Templates`,description:`Save states, workflows, automation, and other project
|
||||
settings into templates.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Baselines And Deviations`,description:`Declare baselines for how your projects progress
|
||||
and zoom in on deviations.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Scheduled Comms`,description:`Schedule reports, notifications, and messages to
|
||||
third-party tools.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Intake Assignees`,description:`Assign approved Intake work items to a member by
|
||||
default.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Custom SLAs`,description:`Set SLA matrices for time-sensitive work items.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Intake Forms`,description:`Take Intake work items from externally accessible web
|
||||
forms.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Emails For Intake`,description:`Get an email address for reporting work items
|
||||
directly into a project's Intake.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}}]},{id:`visualization`,title:`Visualization`,features:[{title:`Layouts`,description:`Choose from the List, the Board, the Calendar, the
|
||||
Gantt, or the Spreadsheet layout for your work items.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Views`,description:`Save sort, filter, and display options on a layout to a
|
||||
view.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Shared Views`,description:`Choose a few members to share a view with.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Publish Views`,description:`Put a view on the Internet and let your customers
|
||||
interact with them.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Dashboards and Widgets`,description:`Create your own dashboards with custom widgets
|
||||
and data types.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}}]},{id:`analytics-reports`,title:`Analytics + reports`,features:[{title:`Progress Charts`,description:`Track progress in cycles, modules, and overviews
|
||||
throughout Plane without switching to dashboards
|
||||
or Analytics.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Cycle Reports`,description:`Get on-demand cycle reports during and after a
|
||||
cycle. Revisit reports anytime from permalinks.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Insights`,description:`Hindsight, On-demand insights, Foresights.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Advanced Pages Analytics`,description:`See who's viewing, sharing, and commenting on
|
||||
your pages along with other useful info.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Custom Reports`,description:`Generate reports by any dimension and metric
|
||||
across your project or workspace.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}}]},{id:`navigation`,title:`Navigation`,features:[{title:`Power K`,description:`Access a keyboard-first gateway to almost anything
|
||||
in Plane.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`PQL`,description:`Write Plane Query Language in search with support
|
||||
for Boolean operators. Soon, you can write natural
|
||||
language queries.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}}]},{id:`workspace-user-management`,title:`Workspace and user management`,features:[{title:`Member limit`,description:`Number of seats that can use project and work management features`,selfHostedDescription:`Number of users that our standard infra supports
|
||||
Increase infra to get more users`,cloud:{free:`12`,one:``,pro:`Unlimited`,business:`Unlimited`,enterprise:`Unlimited`},"self-hosted":{free:`~50`,one:`~50`,pro:`~200`,business:`~200`,enterprise:`Unlimited`}},{title:`Roles`,description:`Choose from one of four pre-defined roles or create
|
||||
custom ones with RBAC.`,cloud:{free:`Basic`,one:`Basic`,pro:`Pre-defined roles`,business:`RBAC`,enterprise:`GAC`}},{title:`Guests`,description:`Let some users see everything or just their work items in
|
||||
a project.`,cloud:{free:!1,one:`5 per paid member`,pro:`5 per paid member`,business:`5 per paid member`,enterprise:`5 per paid member`}},{title:`Approvals`,description:`Set workspace, project, and work item type approvals to
|
||||
designated admins.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Admin Interface`,description:`Get an admin overview to manage workspace and
|
||||
project settings.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Workspace Activity Logs`,description:`See filterable activity logs for your entire
|
||||
workspace.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`API-enabled Audit Logs`,description:`See a full-workspace audit log and use APIs to flag
|
||||
Plane activity in compliance systems.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}}]},{id:`automations-workflows`,title:`Automations and workflows`,features:[{title:`Trigger And Action`,description:`Choose a trigger and a corresponding action per
|
||||
automation flow.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Decisions And Loops Automation`,description:`Use actions as triggers indefinitely in an
|
||||
automation flow.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Number of automations`,description:`Total number of automation flows in your
|
||||
workspace`,cloud:{free:!1,one:!1,pro:`5,000`,business:`10,000`,enterprise:`Unlimited`}}]},{id:`knowledge-management`,title:`Knowledge management`,features:[{title:`Pages`,description:`Build knowledge bases for your teams which are
|
||||
accessible & shareable.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Real-time Collab`,description:`Edit a page together with members in your project,
|
||||
team, or workspace.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Work item Embeds`,description:`Embed work items from any project you are a member
|
||||
of.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Link-to-work items`,description:`Link pages in work items in a separate section in work item
|
||||
details.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Publish`,description:`Put your pages on the web for external users and let
|
||||
them comment without signing into your workspace.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Wiki`,description:`Create company-wide wikis or knowledge bases
|
||||
without creating a project.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Exports`,description:`Export page content into PDFs or Word-compatible
|
||||
docs.`,cloud:{free:!1,one:!1,pro:`One download
|
||||
at a time`,business:`Queued downloads`,enterprise:`Queued downloads`}},{title:`Templates`,description:`Use pages as templates for your project, team, or
|
||||
workspace.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Versions`,description:`See restorable version of edits to your pages.`,cloud:{free:!1,one:!1,pro:`2 days`,business:`3 months`,enterprise:`Unlimited`}},{title:`Databases + Formulas`,description:`Put databases and formulas into a page without
|
||||
worrying about losing text, images, or other content
|
||||
types.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Nested Pages`,description:`Pages inside a page, organize your pages
|
||||
as you see fit for the progressive
|
||||
disclosure.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:`Word-compatible + other format downloads`,enterprise:`Word-compatible + other format downloads`}}]},{id:`importers`,title:`Importers`,features:[{title:`Jira`,description:`Import your work items and members from Jira.`,cloud:{free:`Without custom props`,one:`Without custom props`,pro:`With custom props`,business:`With custom props`,enterprise:`With custom props`}},{title:`GitHub`,description:`Import your work items and members from GitHub.`,cloud:{free:`Without custom props`,one:`Without custom props`,pro:`With custom props`,business:`With custom props`,enterprise:`With custom props`}}]},{id:`integrations`,title:`Integrations`,comingSoon:!0,features:[{title:`GitHub`,description:`Sync Plane work items and states to GitHub work items and
|
||||
states. Update GitHub automatically with activity
|
||||
from Plane and vice-versa.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Slack`,description:`Get Plane activity in Slack and use / commands in
|
||||
Slack to make changes in Plane.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Zapier`,description:`Run if-then-else automations using Zapier.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Zendesk`,description:`Create Plane work items from Zendesk tickets.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Freshdesk`,description:`Create Plane work items from Freshdesk tickets.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}}]},{id:`storage`,title:`Storage`,cloudOnly:!0,features:[{title:`Space`,description:`Total storage allowed per workspace`,cloud:{free:`5GB`,one:!1,pro:`1 TB`,business:`5 TB`,enterprise:`Custom`}},{title:`Max file size`,description:`Limit for uploads to your workspace`,cloud:{free:`5 MB`,one:!1,pro:`100 MB`,business:`200 MB`,enterprise:`Custom`}}]},{id:`security`,title:`Security`,features:[{title:`SAML`,description:`Get the officially supported SAML implementation
|
||||
and make Plane secure with any IdP.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`OIDC`,description:`Get the officiall
|
||||
|
|
@ -5,6 +5,10 @@ import sys
|
|||
import urllib.request
|
||||
|
||||
def extract_or_fetch(filename, out_path, image="makeplane/plane-frontend:stable"):
|
||||
if os.path.exists(out_path) and os.path.getsize(out_path) > 50:
|
||||
print(f"Using existing {filename} on host ({os.path.getsize(out_path)} bytes)")
|
||||
return True
|
||||
|
||||
for base_dir in ["/usr/share/caddy/html/assets", "/usr/share/nginx/html/assets"]:
|
||||
src_container_path = f"{base_dir}/{filename}"
|
||||
try:
|
||||
|
|
@ -18,64 +22,92 @@ def extract_or_fetch(filename, out_path, image="makeplane/plane-frontend:stable"
|
|||
with open(out_path, "wb") as f:
|
||||
f.write(res.stdout)
|
||||
print(f"Extracted {filename} from {image} ({len(res.stdout)} bytes)")
|
||||
return
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
url = f"https://jira.tradhox.com/assets/{filename}"
|
||||
urllib.request.urlretrieve(url, out_path)
|
||||
print(f"Fetched {filename} from {url}")
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
content = resp.read()
|
||||
if len(content) > 50:
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(content)
|
||||
print(f"Fetched {filename} from {url} ({len(content)} bytes)")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Notice: Could not fetch {filename} from {url}: {e}")
|
||||
|
||||
# Fallback stub to satisfy docker-compose volume mounts if needed
|
||||
with open(out_path, "w", encoding="utf-8") as f:
|
||||
f.write("// frontend patch stub\n")
|
||||
print(f"Created fallback stub for {filename}")
|
||||
return False
|
||||
|
||||
def patch_page(file_path):
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
code = f.read()
|
||||
if not os.path.exists(file_path):
|
||||
return
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
code = f.read()
|
||||
|
||||
# 1. Replace Community with Enterprise in top box
|
||||
old_box = "(0,L.jsx)(A,{title:`Community`,description:`Unlimited projects, issues, cycles, modules, pages, and storage`})"
|
||||
new_box = "(0,L.jsx)(A,{title:`Enterprise`,description:`Enterprise Edition Active — Unlimited users, projects, issues, cycles, modules, pages, storage, and all enterprise features unlocked`})"
|
||||
if old_box in code:
|
||||
code = code.replace(old_box, new_box)
|
||||
# 1. Replace Community with Enterprise in top box
|
||||
old_box = "(0,L.jsx)(A,{title:`Community`,description:`Unlimited projects, issues, cycles, modules, pages, and storage`})"
|
||||
new_box = "(0,L.jsx)(A,{title:`Enterprise`,description:`Enterprise Edition Active — Unlimited users, projects, issues, cycles, modules, pages, storage, and all enterprise features unlocked`})"
|
||||
if old_box in code:
|
||||
code = code.replace(old_box, new_box)
|
||||
|
||||
# 2. Update W for enterprise
|
||||
old_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`billed monthly`,yearlyPriceSecondaryDescription:`billed yearly`,buttonCTA:`Talk to Sales`,isActive:!1}"
|
||||
new_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`Active Plan`,yearlyPriceSecondaryDescription:`Active Plan`,buttonCTA:`Active Plan`,isActive:!0}"
|
||||
if old_ent in code:
|
||||
code = code.replace(old_ent, new_ent)
|
||||
# 2. Update W for enterprise
|
||||
old_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`billed monthly`,yearlyPriceSecondaryDescription:`billed yearly`,buttonCTA:`Talk to Sales`,isActive:!1}"
|
||||
new_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`Active Plan`,yearlyPriceSecondaryDescription:`Active Plan`,buttonCTA:`Active Plan`,isActive:!0}"
|
||||
if old_ent in code:
|
||||
code = code.replace(old_ent, new_ent)
|
||||
|
||||
# 3. Update Y pricing descriptions and button
|
||||
old_ph = "p=d?`a user per month`:`Quote on request`,h=r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
|
||||
new_ph = "p=t===o.ENTERPRISE?`Active Lifetime License`:d?`a user per month`:`Quote on request`,h=t===o.ENTERPRISE?`All features unlocked`:r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
|
||||
if old_ph in code:
|
||||
code = code.replace(old_ph, new_ph)
|
||||
# 3. Update Y pricing descriptions and button
|
||||
old_ph = "p=d?`a user per month`:`Quote on request`,h=r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
|
||||
new_ph = "p=t===o.ENTERPRISE?`Active Lifetime License`:d?`a user per month`:`Quote on request`,h=t===o.ENTERPRISE?`All features unlocked`:r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
|
||||
if old_ph in code:
|
||||
code = code.replace(old_ph, new_ph)
|
||||
|
||||
old_btn = "(0,L.jsx)(T,{variant:`primary`,size:`lg`,onClick:()=>{let e=r??`year`,n=l[t][e]??`https://plane.so/talk-to-sales`;window.open(n,`_blank`)},className:`w-full`,children:d?`Upgrade to ${u}`:a(`common.upgrade_cta.talk_to_sales`)})"
|
||||
new_btn = "(0,L.jsx)(T,{variant:t===o.ENTERPRISE?`primary`:`secondary`,size:`lg`,disabled:!0,className:t===o.ENTERPRISE?`w-full opacity-95 cursor-default`:`w-full opacity-60 cursor-default`,children:t===o.ENTERPRISE?`Current Plan (Active)`:`Included in Enterprise`})"
|
||||
if old_btn in code:
|
||||
code = code.replace(old_btn, new_btn)
|
||||
old_btn = "(0,L.jsx)(T,{variant:`primary`,size:`lg`,onClick:()=>{let e=r??`year`,n=l[t][e]??`https://plane.so/talk-to-sales`;window.open(n,`_blank`)},className:`w-full`,children:d?`Upgrade to ${u}`:a(`common.upgrade_cta.talk_to_sales`)})"
|
||||
new_btn = "(0,L.jsx)(T,{variant:t===o.ENTERPRISE?`primary`:`secondary`,size:`lg`,disabled:!0,className:t===o.ENTERPRISE?`w-full opacity-95 cursor-default`:`w-full opacity-60 cursor-default`,children:t===o.ENTERPRISE?`Current Plan (Active)`:`Included in Enterprise`})"
|
||||
if old_btn in code:
|
||||
code = code.replace(old_btn, new_btn)
|
||||
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(code)
|
||||
print(f"Patched page successfully: {file_path} ({len(code)} bytes)")
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(code)
|
||||
print(f"Patched page successfully: {file_path} ({len(code)} bytes)")
|
||||
except Exception as e:
|
||||
print(f"Notice: Could not patch page {file_path}: {e}")
|
||||
|
||||
def patch_layout(file_path):
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
code = f.read()
|
||||
if not os.path.exists(file_path):
|
||||
return
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
code = f.read()
|
||||
|
||||
old_badge = 'onClick:()=>t(!0),"aria-haspopup":`dialog`,"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Community`'
|
||||
new_badge = 'onClick:()=>{},"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Enterprise`'
|
||||
if old_badge in code:
|
||||
code = code.replace(old_badge, new_badge)
|
||||
old_badge = 'onClick:()=>t(!0),"aria-haspopup":`dialog`,"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Community`'
|
||||
new_badge = 'onClick:()=>{},"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Enterprise`'
|
||||
if old_badge in code:
|
||||
code = code.replace(old_badge, new_badge)
|
||||
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(code)
|
||||
print(f"Patched layout successfully: {file_path} ({len(code)} bytes)")
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(code)
|
||||
print(f"Patched layout successfully: {file_path} ({len(code)} bytes)")
|
||||
except Exception as e:
|
||||
print(f"Notice: Could not patch layout {file_path}: {e}")
|
||||
|
||||
def patch_badge(file_path):
|
||||
# The upgrade badge returns null so no "Pro" badges render anywhere
|
||||
new_code = 'import{t as e}from"./jsx-runtime-noaePsda.js";var r=e();function i(e){return null;}export{i as t};\n'
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(new_code)
|
||||
print(f"Patched badge successfully: {file_path} ({len(new_code)} bytes)")
|
||||
try:
|
||||
# The upgrade badge returns null so no "Pro" badges render anywhere
|
||||
new_code = 'import{t as e}from"./jsx-runtime-noaePsda.js";var r=e();function i(e){return null;}export{i as t};\n'
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(new_code)
|
||||
print(f"Patched badge successfully: {file_path} ({len(new_code)} bytes)")
|
||||
except Exception as e:
|
||||
print(f"Notice: Could not patch badge {file_path}: {e}")
|
||||
|
||||
def main():
|
||||
target_dir = sys.argv[1] if len(sys.argv) > 1 else os.path.dirname(os.path.abspath(__file__))
|
||||
|
|
|
|||
1
deploy/patches/upgrade-badge-BxAQzHXC.js
Normal file
1
deploy/patches/upgrade-badge-BxAQzHXC.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{t as e}from"./jsx-runtime-noaePsda.js";var r=e();function i(e){return null;}export{i as t};
|
||||
Loading…
Reference in a new issue