feat(storefront): 显示价=定价×客户折扣率(三类统一)+产线订阅购买链路+修既有控件bug
1) get_customer_price_display 新增(core.py + __init__.py + env 注册)
统一三类产品对外报价口径:显示价 = 资源真实定价 × 客户折扣率
- 产线 pipeline_pricing_map / 存储 storres_pricing_map / 模型 llm_model.ppid
各自解析 ppid → get_pricing_display 取单位价 → × 折扣
- 折扣率解析走 discount 模块 get_min_product_discount(专属优先/'*'兜底/1.0)
>1 加价(存储1.5)、<1 让利(百炼0.9);折扣≠1 时返回 original_text 原价
- 未登录(user_org_id 空)传 '' 只命中 '*' 通用折扣 → 访客可见报价
2) purchase_realtime 加产线分支(product_type=='pipeline')
订阅制:无 usage 表,直接算价→余额预检→product_accounting_generic 实时记账→开订阅权益
charge_mode month/year,entitlement.duration_days 30/365
3) 展示页 index.ui:接 get_customer_price_display;产线卡显示月/年价+原价划线+订阅态文案;
按钮按订阅状态分流(已订阅→进入产线 / 未订阅→立即购买);product_code 兼容
opportunity_general/bidding_general/sdlc_general 与旧 PIPE-* 两套映射
4) 修两个既有 bug(购买页原本就坏):
- InputNumber 控件 bricks 未注册 → widgetBuild 返回 null 静默不渲染,改 UiInt
- 支付脚本 getWidgetById 缺第二参数(查找起点) → from_widget undefined 抛 TypeError
点击支付无反应,补 bricks.app.root;UiType 取值 .value → resultValue()
- UiType 系控件同时传 name(表单/getValue)+id(set_id 覆盖 DOM id 供 getWidgetById)
本地 mock 渲染测试 6 项全过:三类价格/订阅态分流/规划中占位/未登录访客可见价无购买按钮
This commit is contained in:
parent
275806c1cc
commit
1f399d6b00
@ -91,6 +91,12 @@ async def get_product_display_info(product_id=None, product_code=None):
|
||||
return await manager.get_product_display_info(product_id, product_code)
|
||||
|
||||
|
||||
async def get_customer_price_display(product_id=None, product_code=None, user_org_id=None):
|
||||
"""客户视角展示价 = 资源真实定价 × 客户折扣率(产线/存储/模型三类统一口径)。"""
|
||||
manager = get_manager()
|
||||
return await manager.get_customer_price_display(product_id, product_code, user_org_id)
|
||||
|
||||
|
||||
async def check_product_availability(product_id=None, product_code=None, user_org_id=None):
|
||||
"""Check product availability via resource module interface."""
|
||||
manager = get_manager()
|
||||
@ -182,6 +188,7 @@ def load_product_management():
|
||||
env.import_categories_and_products = import_categories_and_products
|
||||
# Resource module interface dispatchers
|
||||
env.get_product_display_info = get_product_display_info
|
||||
env.get_customer_price_display = get_customer_price_display
|
||||
env.check_product_availability = check_product_availability
|
||||
env.check_product_consumable = check_product_consumable
|
||||
env.execute_product = execute_product
|
||||
|
||||
@ -1293,6 +1293,142 @@ class ProductManager:
|
||||
return {'success': False, 'message': err}
|
||||
return await fn(ref_id)
|
||||
|
||||
async def get_customer_price_display(self, product_id=None, product_code=None,
|
||||
user_org_id=None):
|
||||
"""客户视角展示价 = 资源真实定价 × 客户折扣率(2026-09-06 新增)。
|
||||
|
||||
统一三类产品的对外报价口径(用户明确要求「产品显示的价格要用定价里
|
||||
价格×客户的折扣率」):
|
||||
产线 pipeline_pricing_map → ppid → 单位价(元/月) × 折扣
|
||||
存储 storres_pricing_map → ppid → 单位价(元/GB月) × 折扣
|
||||
模型 llm_model.ppid → ppid → 单位价(元/百万tokens 等) × 折扣
|
||||
|
||||
客户折扣率解析(discount 模块,专属优先 / '*' 兜底 / 无记录 1.0):
|
||||
>1 = 加价卖给客户(如存储 1.5),<1 = 让利(如百炼模型 0.9)。
|
||||
|
||||
未登录(user_org_id 空)时用 '*' 通用折扣报价——展示页对访客可见。
|
||||
|
||||
Returns:
|
||||
{'success', 'prices': [{'label','unit_price','unit_label','amount','unit'}],
|
||||
'pricing_text', 'discount', 'original_text'}
|
||||
"""
|
||||
iface, product, err = await self._get_product_interface(
|
||||
product_id, product_code)
|
||||
if err:
|
||||
return {'success': False, 'message': err}
|
||||
product = product or {}
|
||||
ref_id, err = self._get_ref_id(product)
|
||||
if err:
|
||||
return {'success': False, 'message': err}
|
||||
|
||||
env = ServerEnv()
|
||||
product_type = product.get('product_type', '') or ''
|
||||
pid = product.get('id') or ''
|
||||
resellerid = product.get('org_id') or '0'
|
||||
|
||||
# 1. 客户折扣率(未登录 → 传 '' 让 discount 模块只命中 '*' 兜底)
|
||||
discount = 1.0
|
||||
try:
|
||||
d = await env.get_min_product_discount(pid, resellerid, user_org_id or '')
|
||||
if d not in (None, '', 0):
|
||||
discount = float(d)
|
||||
except Exception:
|
||||
discount = 1.0
|
||||
|
||||
# 2. 按产品类型解析 ppid 列表 [(charge_mode/meter_mode 标签, ppid)]
|
||||
dbname = self._get_dbname()
|
||||
ppid_pairs = []
|
||||
try:
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
if product_type == 'pipeline':
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT charge_mode, ppid, months, valid_days FROM pipeline_pricing_map "
|
||||
"WHERE pipeline_id=${rid}$ AND status='active' ORDER BY months",
|
||||
{'rid': ref_id})
|
||||
for r in (recs or []):
|
||||
cm = str(getattr(r, 'charge_mode', '') or '')
|
||||
ppid_pairs.append(('包年' if cm == 'year' else '包月',
|
||||
getattr(r, 'ppid', '') or ''))
|
||||
elif product_type == 'workspace_storage':
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT meter_mode, ppid FROM storres_pricing_map "
|
||||
"WHERE spec_id=${rid}$ AND status='active'",
|
||||
{'rid': ref_id})
|
||||
seen = set()
|
||||
for r in (recs or []):
|
||||
ppid = getattr(r, 'ppid', '') or ''
|
||||
if ppid and ppid not in seen:
|
||||
seen.add(ppid)
|
||||
ppid_pairs.append(('容量', ppid))
|
||||
else:
|
||||
# 模型类(pipeline_llm_model / llm_model):ppid 挂模型表
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT ppid FROM llm_model WHERE id=${rid}$", {'rid': ref_id})
|
||||
if recs:
|
||||
ppid = getattr(recs[0], 'ppid', '') or ''
|
||||
if ppid:
|
||||
ppid_pairs.append(('按量', ppid))
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
except Exception as e:
|
||||
debug(f'get_customer_price_display: ppid 解析失败 {pid}: {e}')
|
||||
|
||||
if not ppid_pairs:
|
||||
return {'success': False, 'message': '未配置定价方案',
|
||||
'prices': [], 'pricing_text': '未配置定价',
|
||||
'discount': discount, 'original_text': ''}
|
||||
|
||||
# 3. 取定价单位价 → × 折扣率
|
||||
prices = []
|
||||
for label, ppid in ppid_pairs:
|
||||
if not ppid:
|
||||
continue
|
||||
try:
|
||||
pd = await env.get_pricing_display(ppid)
|
||||
except Exception:
|
||||
pd = None
|
||||
if not pd:
|
||||
continue
|
||||
for item in (pd.get('items') or []):
|
||||
for pf in (item.get('price_factors') or []):
|
||||
up = pf.get('unit_price')
|
||||
if up is None:
|
||||
continue
|
||||
up = float(up)
|
||||
prices.append({
|
||||
'label': label,
|
||||
'factor': pf.get('factor', ''),
|
||||
'factor_label': pf.get('label', ''),
|
||||
'unit': pf.get('unit', ''),
|
||||
'unit_label': pf.get('unit_label', ''),
|
||||
'original_price': round(up, 6),
|
||||
'amount': round(up * discount, 6),
|
||||
})
|
||||
|
||||
if not prices:
|
||||
return {'success': False, 'message': '定价数据为空',
|
||||
'prices': [], 'pricing_text': '未配置定价',
|
||||
'discount': discount, 'original_text': ''}
|
||||
|
||||
# 4. 拼展示文本(折扣≠1 时同时给原价,让客户看见让利/加价幅度)
|
||||
parts = []
|
||||
orig_parts = []
|
||||
for p in prices:
|
||||
head = p['label'] if p['label'] and p['label'] not in ('按量', '容量') else ''
|
||||
seg = ('%s ' % head if head else '') + '¥%.4g %s' % (
|
||||
p['amount'], p['unit_label'] or '元')
|
||||
parts.append(seg.strip())
|
||||
if abs(discount - 1.0) > 1e-9:
|
||||
orig_parts.append('%.4g' % p['original_price'])
|
||||
|
||||
pricing_text = ' | '.join(parts)
|
||||
original_text = ''
|
||||
if orig_parts:
|
||||
original_text = '原价 %s %s' % (
|
||||
' / '.join(orig_parts), prices[0]['unit_label'] or '元')
|
||||
|
||||
return {'success': True, 'prices': prices, 'pricing_text': pricing_text,
|
||||
'original_text': original_text, 'discount': discount}
|
||||
|
||||
async def check_product_availability(self, product_id=None, product_code=None,
|
||||
user_org_id=None):
|
||||
"""检查产品可用性(通过资源模块接口)。"""
|
||||
@ -1507,6 +1643,19 @@ class ProductManager:
|
||||
'duration_days': duration_days}
|
||||
usage_data = {'meter_mode': 'direct', 'storage_gb': storage_gb,
|
||||
'valid_months': valid_months}
|
||||
|
||||
elif product_type == 'pipeline':
|
||||
# 产线使用费(订阅制):无 usage 表,直接算价+实时记账+开权益
|
||||
# (2026-09-06 产线产品化:月付 months=1 / 年付 months=10=月费×10)
|
||||
from pipeline_core.pipeline_pricing import calculate_pipeline_amount
|
||||
charging_module = 'pipeline'
|
||||
cm = charge_mode or 'month'
|
||||
if cm not in ('month', 'year'):
|
||||
return {'success': False, 'message': '产线只支持 charge_mode=month/year'}
|
||||
amount, ppid, months, duration_days = await calculate_pipeline_amount(ref_id, cm)
|
||||
entitlement = {'duration_days': duration_days, 'charge_mode': cm,
|
||||
'pipeline_id': ref_id}
|
||||
usage_data = {'charge_mode': cm, 'months': months}
|
||||
else:
|
||||
return {'success': False,
|
||||
'message': f'产品类型({product_type})暂不支持实时购买'}
|
||||
@ -1527,11 +1676,17 @@ class ProductManager:
|
||||
raise Exception(f'余额不足:可用 {balance:.2f},应付 {sell_amount:.2f},请先充值')
|
||||
|
||||
# ── 实时复式记账(内部写 biz_order + 分录,余额不足会抛 AccountOverDraw)──
|
||||
rec_obj = DictObject(**usage_rec) if isinstance(usage_rec, dict) else usage_rec
|
||||
if charging_module == 'account':
|
||||
acc = await self.account_resource_accounting(rec_obj)
|
||||
if charging_module == 'pipeline':
|
||||
# 产线无 usage 表:直接走通用落账(product_accounting_generic
|
||||
# 内部再算一次价含客户折扣,与预检同口径)
|
||||
acc = await self.product_accounting_generic(
|
||||
product.get('id'), usage_data, user_org_id, user_id)
|
||||
else:
|
||||
acc = await self.storage_resource_accounting(rec_obj)
|
||||
rec_obj = DictObject(**usage_rec) if isinstance(usage_rec, dict) else usage_rec
|
||||
if charging_module == 'account':
|
||||
acc = await self.account_resource_accounting(rec_obj)
|
||||
else:
|
||||
acc = await self.storage_resource_accounting(rec_obj)
|
||||
if not acc or not acc.get('success'):
|
||||
raise Exception(f'记账失败: {acc}')
|
||||
|
||||
@ -1548,6 +1703,7 @@ class ProductManager:
|
||||
except Exception as e:
|
||||
exception(f'purchase_realtime failed: {e}')
|
||||
# 回滚:删除已写的待记账用量记录,避免残留半截账
|
||||
# (产线 charging_module='pipeline' 无 usage 表,usage_rec 恒 None 天然跳过)
|
||||
if usage_rec is not None:
|
||||
try:
|
||||
rid = usage_rec.get('id') if isinstance(usage_rec, dict) \
|
||||
|
||||
@ -50,7 +50,11 @@ else:
|
||||
|
||||
rows.append(_row('产品名称', getattr(p, 'product_name', ''), '#222', True))
|
||||
|
||||
# 参数输入区(存储需容量+月数;账号走默认月租)
|
||||
# 参数输入区(存储需容量+月数;产线选月付/年付;账号走默认月租)
|
||||
# ⚠️ 控件名必须是 bricks 实际注册的:数字输入 = UiInt(不是 InputNumber,
|
||||
# 该名未注册 → widgetBuild 返回 null,控件静默不渲染,2026-09-06 修复)。
|
||||
# UiType 系控件同时传 name + id:name 供 getValue/表单,id 供 getWidgetById 定位
|
||||
# (widgetBuild 用 set_id(desc.id) 覆盖 DOM id)。
|
||||
param_widgets = []
|
||||
if product_type == 'workspace_storage':
|
||||
param_widgets.append({
|
||||
@ -59,8 +63,8 @@ else:
|
||||
'subwidgets': [
|
||||
{'widgettype': 'Text', 'options': {
|
||||
'text': '购买容量(GB)', 'width': '90px', 'fontSize': '13px', 'color': '#666'}},
|
||||
{'widgettype': 'InputNumber', 'id': 'buy_storage_gb',
|
||||
'options': {'value': 100, 'min': 1, 'step': 10, 'width': '120px'}}
|
||||
{'widgettype': 'UiInt', 'id': 'buy_storage_gb',
|
||||
'options': {'name': 'buy_storage_gb', 'value': 100, 'width': '120px'}}
|
||||
]
|
||||
})
|
||||
param_widgets.append({
|
||||
@ -69,11 +73,26 @@ else:
|
||||
'subwidgets': [
|
||||
{'widgettype': 'Text', 'options': {
|
||||
'text': '购买月数', 'width': '90px', 'fontSize': '13px', 'color': '#666'}},
|
||||
{'widgettype': 'InputNumber', 'id': 'buy_valid_months',
|
||||
'options': {'value': 1, 'min': 1, 'max': 12, 'step': 1, 'width': '120px'}}
|
||||
{'widgettype': 'UiInt', 'id': 'buy_valid_months',
|
||||
'options': {'name': 'buy_valid_months', 'value': 1, 'width': '120px'}}
|
||||
]
|
||||
})
|
||||
rows.append(_row('计费方式', '按容量直购(元/GB月)', '#666'))
|
||||
elif product_type == 'pipeline':
|
||||
# 产线使用费:月付 / 年付(年费=月费×10,即付10个月用12个月)
|
||||
param_widgets.append({
|
||||
'widgettype': 'HBox',
|
||||
'options': {'padding': '8px 0', 'gap': '8px', 'alignItems': 'center'},
|
||||
'subwidgets': [
|
||||
{'widgettype': 'Text', 'options': {
|
||||
'text': '订阅周期', 'width': '90px', 'fontSize': '13px', 'color': '#666'}},
|
||||
{'widgettype': 'UiCode', 'id': 'buy_charge_mode',
|
||||
'options': {'name': 'buy_charge_mode', 'value': 'month', 'width': '200px',
|
||||
'data': [{'value': 'month', 'text': '包月(30天)'},
|
||||
{'value': 'year', 'text': '包年(365天,付10个月)'}]}}
|
||||
]
|
||||
})
|
||||
rows.append(_row('计费方式', '订阅制使用费(模型/存储消耗另按量计费)', '#666'))
|
||||
elif product_type == 'account':
|
||||
rows.append(_row('计费方式', '订阅制(月/年/试用)', '#666'))
|
||||
else:
|
||||
@ -89,7 +108,12 @@ else:
|
||||
rows.append(_row('账户余额', balance_text, balance_color, True))
|
||||
|
||||
# 确认支付按钮:script+fetch 提交,确保读取输入框当前值
|
||||
# ⚠️ getWidgetById 必须传第二参数(查找起点):bricks.getWidgetById(idset, from_widget)
|
||||
# 里 get_by_id 直接读 fromw.dom_element,from_widget 为 undefined 会抛 TypeError
|
||||
# → 点击支付无任何反应(既有 bug,2026-09-06 修复)。用 bricks.app.root 作起点。
|
||||
# UiInt 取值用 resultValue()(返回 int),UiCode 取值用 .value(返回字符串)。
|
||||
is_storage = (product_type == 'workspace_storage')
|
||||
is_pipeline = (product_type == 'pipeline')
|
||||
pay_script = (
|
||||
"var pid='" + product_id + "';"
|
||||
"var params=new URLSearchParams();"
|
||||
@ -97,13 +121,20 @@ else:
|
||||
)
|
||||
if is_storage:
|
||||
pay_script += (
|
||||
"var gb=bricks.getWidgetById('buy_storage_gb');"
|
||||
"var mo=bricks.getWidgetById('buy_valid_months');"
|
||||
"var gbv=gb?gb.value:100; var mov=mo?mo.value:1;"
|
||||
"var gb=bricks.getWidgetById('buy_storage_gb',bricks.app.root);"
|
||||
"var mo=bricks.getWidgetById('buy_valid_months',bricks.app.root);"
|
||||
"var gbv=gb?gb.resultValue():100; var mov=mo?mo.resultValue():1;"
|
||||
"if(!gbv||gbv<=0){alert('请输入购买容量');return;}"
|
||||
"if(!mov||mov<=0){alert('请输入购买月数');return;}"
|
||||
"params.append('storage_gb',gbv);"
|
||||
"params.append('valid_months',mov);"
|
||||
)
|
||||
if is_pipeline:
|
||||
pay_script += (
|
||||
"var cm=bricks.getWidgetById('buy_charge_mode',bricks.app.root);"
|
||||
"var cmv=cm?(cm.value||'month'):'month';"
|
||||
"params.append('charge_mode',cmv);"
|
||||
)
|
||||
pay_script += (
|
||||
"var btn=this; btn.disabled=true;"
|
||||
"fetch('/product_management/storefront/api/purchase_realtime.dspy',"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{% set cats_r = get_category_tree(org_id='0') %}
|
||||
{% set cats = (cats_r or {}).get('tree', []) if (cats_r or {}).get('success') else [] %}
|
||||
{% set userid = get_user() %}
|
||||
{% set userorgid = get_userorgid() %}
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
@ -50,21 +51,17 @@
|
||||
{% set ns = namespace(first=true) %}
|
||||
{% for p in prods %}
|
||||
{% if p.get('is_active', true) %}
|
||||
{% set disp = get_product_display_info(p.get('id')) %}
|
||||
{% set pricing_text = (disp or {}).get('pricing_text', '定价暂不可用') if (disp or {}).get('success') else '定价暂不可用' %}
|
||||
{# 显示价 = 资源真实定价 × 客户折扣率(2026-09-06):未登录按 '*' 通用折扣报价 #}
|
||||
{% set cust = get_customer_price_display(p.get('id'), user_org_id=userorgid or '') %}
|
||||
{% set pricing_text = (cust or {}).get('pricing_text', '定价暂不可用') if (cust or {}).get('success') else ((cust or {}).get('message') or '定价暂不可用') %}
|
||||
{% set original_text = (cust or {}).get('original_text', '') %}
|
||||
{% set is_pipeline = (p.get('product_type') == 'pipeline') %}
|
||||
{% set extra = {} %}
|
||||
{% if is_pipeline %}
|
||||
{% set extra = {'tab_name': 'pipeline_card_' + str(p.get('product_code','')), 'tab_label': p.get('product_name') or '', 'url': '', 'planned': False, 'message': ''} %}
|
||||
{% if (p.get('product_code') or '') == 'PIPE-OPP' %}
|
||||
{% set _ = extra.update({'url': '/pipeline-opportunity/agent'}) %}
|
||||
{% elif (p.get('product_code') or '') == 'PIPE-BID' %}
|
||||
{% set _ = extra.update({'url': '/pipeline-bidding/agent'}) %}
|
||||
{% elif (p.get('product_code') or '') == 'PIPE-DEV' %}
|
||||
{% set _ = extra.update({'url': '/pipeline_core/agent'}) %}
|
||||
{% else %}
|
||||
{% set _ = extra.update({'planned': True, 'message': (p.get('product_name') or '') + '规划中,尚未开发'}) %}
|
||||
{% endif %}
|
||||
{# 产线订阅状态:已订阅→「进入产线」直达;未订阅→「立即购买」弹购买确认 #}
|
||||
{% set sub = check_subscription_valid(p.get('id'), userorgid or '') if userorgid else {'valid': False} %}
|
||||
{% set _urls = {'PIPE-OPP': '/pipeline-opportunity/agent', 'PIPE-BID': '/pipeline-bidding/agent', 'PIPE-DEV': '/pipeline_core/agent', 'opportunity_general': '/pipeline-opportunity/agent', 'bidding_general': '/pipeline-bidding/agent', 'sdlc_general': '/pipeline_core/agent'} %}
|
||||
{% set extra = {'tab_name': 'pipeline_card_' + str(p.get('product_code','')), 'tab_label': p.get('product_name') or '', 'url': _urls.get(p.get('product_code') or '', ''), 'planned': not _urls.get(p.get('product_code') or '', ''), 'message': (p.get('product_name') or '') + '规划中,尚未开发', 'subscribed': (sub or {}).get('valid', False), 'sub_end': (sub or {}).get('end_date', '')} %}
|
||||
{% endif %}
|
||||
{% if not ns.first %},{% endif %}
|
||||
{% set ns.first = false %}
|
||||
@ -147,20 +144,14 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{% if is_pipeline %}
|
||||
{% if is_pipeline and extra.planned %}
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
{% if extra.planned %}
|
||||
"text": "规划中",
|
||||
"fontSize": "14px",
|
||||
"color": "#94a3b8",
|
||||
"fontWeight": "600"
|
||||
{% else %}
|
||||
"text": "订阅制",
|
||||
"fontSize": "14px",
|
||||
"color": "#e74c3c"
|
||||
{% endif %}
|
||||
}
|
||||
},
|
||||
{% else %}
|
||||
@ -172,6 +163,27 @@
|
||||
"color": "#e74c3c"
|
||||
}
|
||||
},
|
||||
{% if original_text %}
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": {{json.dumps(original_text, ensure_ascii=False)}},
|
||||
"fontSize": "11px",
|
||||
"color": "#94a3b8",
|
||||
"textDecoration": "line-through"
|
||||
}
|
||||
},
|
||||
{% endif %}
|
||||
{% if is_pipeline %}
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": {{json.dumps(('订阅制 · 已订阅至 ' + extra.sub_end) if extra.subscribed else '订阅制 · 模型/存储消耗另计', ensure_ascii=False)}},
|
||||
"fontSize": "11px",
|
||||
"color": {{json.dumps('#27ae60' if extra.subscribed else '#94a3b8')}}
|
||||
}
|
||||
},
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
@ -180,8 +192,7 @@
|
||||
"justifyContent": "flex-end"
|
||||
},
|
||||
"subwidgets": [
|
||||
{% if is_pipeline %}
|
||||
{% if extra.planned %}
|
||||
{% if is_pipeline and extra.planned %}
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
@ -190,7 +201,7 @@
|
||||
"color": "#94a3b8"
|
||||
}
|
||||
}
|
||||
{% else %}
|
||||
{% elif is_pipeline and extra.subscribed %}
|
||||
{
|
||||
"widgettype": "Button",
|
||||
"options": {
|
||||
@ -210,7 +221,6 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
{% endif %}
|
||||
{% elif userid %}
|
||||
{
|
||||
"widgettype": "Button",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user