@designbasekorea/figma-ui 0.6.10 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.css.map +1 -1
- package/dist/index.esm.js +299 -66
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +298 -65
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
37
37
|
remainingShort: '남음',
|
|
38
38
|
usageCountCaption: '오늘 사용',
|
|
39
39
|
remainingCopy: '{{count}}회 남음 · 매일 리셋',
|
|
40
|
+
lowUsageWarning: '무료 사용량이 {{count}}회({{percent}}%) 남았습니다. 멤버십으로 무제한 이용해보세요.',
|
|
40
41
|
usageUnavailable: '사용량 제한이 없습니다',
|
|
41
42
|
unlimitedUsageTooltip: '모든 기능을 제한 없이 사용할 수 있습니다',
|
|
42
43
|
betaUsageDescription: '베타 기간에는 사용량 제한이 적용되지 않습니다',
|
|
@@ -51,12 +52,16 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
51
52
|
'form.submit': '제출',
|
|
52
53
|
'form.verifying': '확인 중...',
|
|
53
54
|
'license.youtube_membership': 'YouTube 멤버십',
|
|
55
|
+
'license.current_plan': '현재 플랜',
|
|
56
|
+
'license.pro_plan': '프로 플랜',
|
|
57
|
+
'license.membership': '멤버십',
|
|
54
58
|
'license.pro_account': '프로 계정',
|
|
55
59
|
'license.upgrade_to_pro': '프로로 업그레이드',
|
|
56
60
|
'license.all_features_available': '모든 기능을 사용할 수 있습니다.',
|
|
57
61
|
'license.youtube_membership_description': 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.',
|
|
58
62
|
'license.purchase_for_unlimited': '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.',
|
|
59
63
|
'license.usage_unlimited': '사용 횟수 제한 없음',
|
|
64
|
+
'license.active_until_expiry': '만료일까지 모든 기능을 제한 없이 사용할 수 있습니다.',
|
|
60
65
|
'license.usage_daily': '오늘 남은 횟수 {{remaining}}/{{limit}}',
|
|
61
66
|
'license.usage_weekly': '이번 주 남은 횟수 {{remaining}}/{{limit}}',
|
|
62
67
|
'license.usage_monthly': '이번 달 남은 횟수 {{remaining}}/{{limit}}',
|
|
@@ -66,6 +71,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
66
71
|
'license.spots_remaining': '자리 남음',
|
|
67
72
|
'license.deactivating': '비활성화 중...',
|
|
68
73
|
'license.deactivate': '라이선스 비활성화',
|
|
74
|
+
'license.deactivate_hint': '이 기기에서 라이선스 연결을 해제합니다.',
|
|
69
75
|
'license.designbase_account': '디자인베이스 계정',
|
|
70
76
|
'license.designbase_account_hint': 'WordPress 계정으로 로그인하거나 멤버십을 구매하면 플러그인이 자동으로 활성화됩니다.',
|
|
71
77
|
'license.authenticating': '인증 중...',
|
|
@@ -169,6 +175,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
169
175
|
remainingShort: 'left',
|
|
170
176
|
usageCountCaption: 'Used today',
|
|
171
177
|
remainingCopy: '{{count}} remaining · resets daily',
|
|
178
|
+
lowUsageWarning: 'Only {{count}} free uses ({{percent}}%) remain. Upgrade for unlimited access.',
|
|
172
179
|
usageUnavailable: 'No usage limit',
|
|
173
180
|
unlimitedUsageTooltip: 'Use every feature without limits',
|
|
174
181
|
betaUsageDescription: 'Usage limits are not applied during the beta period',
|
|
@@ -183,12 +190,16 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
183
190
|
'form.submit': 'Activate',
|
|
184
191
|
'form.verifying': 'Verifying...',
|
|
185
192
|
'license.youtube_membership': 'YouTube Membership',
|
|
193
|
+
'license.current_plan': 'Current plan',
|
|
194
|
+
'license.pro_plan': 'Pro plan',
|
|
195
|
+
'license.membership': 'Membership',
|
|
186
196
|
'license.pro_account': 'Pro account',
|
|
187
197
|
'license.upgrade_to_pro': 'Upgrade to Pro',
|
|
188
198
|
'license.all_features_available': 'All features are available.',
|
|
189
199
|
'license.youtube_membership_description': 'Join YouTube Membership to use Designbase plugins without limits.',
|
|
190
200
|
'license.purchase_for_unlimited': 'Get unlimited access with a Designbase membership or license.',
|
|
191
201
|
'license.usage_unlimited': 'Unlimited usage',
|
|
202
|
+
'license.active_until_expiry': 'All features remain available without limits until expiration.',
|
|
192
203
|
'license.usage_daily': '{{remaining}}/{{limit}} remaining today',
|
|
193
204
|
'license.usage_weekly': '{{remaining}}/{{limit}} remaining this week',
|
|
194
205
|
'license.usage_monthly': '{{remaining}}/{{limit}} remaining this month',
|
|
@@ -198,6 +209,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
198
209
|
'license.spots_remaining': 'spots remaining',
|
|
199
210
|
'license.deactivating': 'Deactivating...',
|
|
200
211
|
'license.deactivate': 'Deactivate license',
|
|
212
|
+
'license.deactivate_hint': 'Disconnects this license from the current device.',
|
|
201
213
|
'license.designbase_account': 'Designbase account',
|
|
202
214
|
'license.designbase_account_hint': 'Sign in with your WordPress account or purchase a membership to activate the plugin automatically.',
|
|
203
215
|
'license.authenticating': 'Authenticating...',
|
|
@@ -301,6 +313,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
301
313
|
remainingShort: '残り',
|
|
302
314
|
usageCountCaption: '今日の使用',
|
|
303
315
|
remainingCopy: '{{count}}回残り · 毎日リセット',
|
|
316
|
+
lowUsageWarning: '無料使用回数は残り{{count}}回({{percent}}%)です。無制限利用にアップグレードできます。',
|
|
304
317
|
usageUnavailable: '使用制限はありません',
|
|
305
318
|
unlimitedUsageTooltip: 'すべての機能を無制限で使用できます',
|
|
306
319
|
betaUsageDescription: 'ベータ期間中は使用制限がありません',
|
|
@@ -315,12 +328,16 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
315
328
|
'form.submit': '有効化',
|
|
316
329
|
'form.verifying': '確認中...',
|
|
317
330
|
'license.youtube_membership': 'YouTubeメンバーシップ',
|
|
331
|
+
'license.current_plan': '現在のプラン',
|
|
332
|
+
'license.pro_plan': 'Proプラン',
|
|
333
|
+
'license.membership': 'メンバーシップ',
|
|
318
334
|
'license.pro_account': 'Proアカウント',
|
|
319
335
|
'license.upgrade_to_pro': 'Proにアップグレード',
|
|
320
336
|
'license.all_features_available': 'すべての機能を使用できます。',
|
|
321
337
|
'license.youtube_membership_description': 'YouTubeメンバーシップに参加すると、Designbaseプラグインを無制限で使用できます。',
|
|
322
338
|
'license.purchase_for_unlimited': 'Designbaseのメンバーシップまたはライセンスで無制限に利用できます。',
|
|
323
339
|
'license.usage_unlimited': '使用回数の制限なし',
|
|
340
|
+
'license.active_until_expiry': '有効期限まで、すべての機能を制限なく使用できます。',
|
|
324
341
|
'license.usage_daily': '今日の残り {{remaining}}/{{limit}}回',
|
|
325
342
|
'license.usage_weekly': '今週の残り {{remaining}}/{{limit}}回',
|
|
326
343
|
'license.usage_monthly': '今月の残り {{remaining}}/{{limit}}回',
|
|
@@ -330,6 +347,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
330
347
|
'license.spots_remaining': '枠残り',
|
|
331
348
|
'license.deactivating': '無効化中...',
|
|
332
349
|
'license.deactivate': 'ライセンスを無効化',
|
|
350
|
+
'license.deactivate_hint': 'このデバイスからライセンスの接続を解除します。',
|
|
333
351
|
'license.designbase_account': 'Designbaseアカウント',
|
|
334
352
|
'license.designbase_account_hint': 'WordPressアカウントでサインインするか、メンバーシップを購入するとプラグインが自動的に有効化されます。',
|
|
335
353
|
'license.authenticating': '認証中...',
|
|
@@ -978,7 +996,7 @@ const fallbackText = (t, key, fallback) => {
|
|
|
978
996
|
const translated = t?.(key);
|
|
979
997
|
return translated && translated !== key ? translated : fallback;
|
|
980
998
|
};
|
|
981
|
-
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, locale, className, }) => {
|
|
999
|
+
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, remainingUsage, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, licenseSource, accessScope, expiresAt, lastVerifiedAt, t, locale, className, }) => {
|
|
982
1000
|
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
983
1001
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
984
1002
|
const rootRef = React.useRef(null);
|
|
@@ -988,9 +1006,11 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
988
1006
|
const isBeta = paymentStatus === 'BETA';
|
|
989
1007
|
const hasDailyLimit = !isPaid && maxDailyUsage > 0;
|
|
990
1008
|
const limit = Math.max(1, Math.round(maxDailyUsage));
|
|
991
|
-
const
|
|
992
|
-
const remaining =
|
|
993
|
-
|
|
1009
|
+
const hasRemainingUsage = typeof remainingUsage === 'number' && Number.isFinite(remainingUsage);
|
|
1010
|
+
const remaining = hasRemainingUsage
|
|
1011
|
+
? Math.min(limit, Math.max(0, Math.round(remainingUsage)))
|
|
1012
|
+
: Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(usageCount))));
|
|
1013
|
+
const remainingPercent = hasDailyLimit ? Math.round((remaining / limit) * 100) : 0;
|
|
994
1014
|
const hasControl = showUsage || showPlan;
|
|
995
1015
|
const planLabel = isPaid
|
|
996
1016
|
? fallbackText(translate, 'proUser', '프로')
|
|
@@ -999,9 +1019,12 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
999
1019
|
: paymentStatus === 'TRIAL'
|
|
1000
1020
|
? fallbackText(translate, 'trialPlan', '체험판')
|
|
1001
1021
|
: fallbackText(translate, 'free', '무료');
|
|
1002
|
-
const
|
|
1022
|
+
const planSuffix = fallbackText(translate, 'planSuffix', '플랜');
|
|
1023
|
+
const normalizedPlanLabel = planLabel.toLocaleLowerCase();
|
|
1024
|
+
const normalizedPlanSuffix = planSuffix.toLocaleLowerCase();
|
|
1025
|
+
const planTitle = isBeta || normalizedPlanLabel.includes(normalizedPlanSuffix)
|
|
1003
1026
|
? planLabel
|
|
1004
|
-
: `${planLabel} ${
|
|
1027
|
+
: `${planLabel} ${planSuffix}`;
|
|
1005
1028
|
const triggerLabel = isPaid
|
|
1006
1029
|
? `${planLabel}, ${fallbackText(translate, 'unlimitedUsage', '무제한 사용 가능')}`
|
|
1007
1030
|
: isBeta
|
|
@@ -1012,6 +1035,41 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
1012
1035
|
const upgradeLabel = isPaid
|
|
1013
1036
|
? fallbackText(translate, 'managePlan', '플랜 관리')
|
|
1014
1037
|
: fallbackText(translate, 'upgradePlan', '플랜 업그레이드');
|
|
1038
|
+
const resolvedLocale = locale || 'ko';
|
|
1039
|
+
const formatDate = (value) => {
|
|
1040
|
+
if (!value)
|
|
1041
|
+
return undefined;
|
|
1042
|
+
const date = new Date(value);
|
|
1043
|
+
if (Number.isNaN(date.getTime()))
|
|
1044
|
+
return value;
|
|
1045
|
+
return new Intl.DateTimeFormat(resolvedLocale, { dateStyle: 'medium' }).format(date);
|
|
1046
|
+
};
|
|
1047
|
+
const formattedSource = licenseSource === 'youtube_membership' || licenseSource === 'membership'
|
|
1048
|
+
? fallbackText(translate, 'license.source_youtube_membership', 'YouTube 멤버십')
|
|
1049
|
+
: licenseSource === 'lemonsqueezy'
|
|
1050
|
+
? fallbackText(translate, 'license.source_purchase', '구매 라이선스')
|
|
1051
|
+
: licenseSource;
|
|
1052
|
+
const formattedScope = accessScope === 'all-products' || accessScope === 'all_products'
|
|
1053
|
+
? fallbackText(translate, 'license.all_plugins', '모든 플러그인')
|
|
1054
|
+
: accessScope;
|
|
1055
|
+
const paidMetadata = [
|
|
1056
|
+
formattedSource && {
|
|
1057
|
+
label: fallbackText(translate, 'license.membership', '멤버십'),
|
|
1058
|
+
value: formattedSource,
|
|
1059
|
+
},
|
|
1060
|
+
expiresAt && {
|
|
1061
|
+
label: fallbackText(translate, 'license.expires', '만료일'),
|
|
1062
|
+
value: formatDate(expiresAt),
|
|
1063
|
+
},
|
|
1064
|
+
formattedScope && {
|
|
1065
|
+
label: fallbackText(translate, 'license.access', '접근 범위'),
|
|
1066
|
+
value: formattedScope,
|
|
1067
|
+
},
|
|
1068
|
+
lastVerifiedAt && {
|
|
1069
|
+
label: fallbackText(translate, 'license.last_checked', '최근 확인'),
|
|
1070
|
+
value: formatDate(lastVerifiedAt),
|
|
1071
|
+
},
|
|
1072
|
+
].filter(Boolean);
|
|
1015
1073
|
React.useEffect(() => {
|
|
1016
1074
|
if (!isOpen)
|
|
1017
1075
|
return undefined;
|
|
@@ -1046,9 +1104,12 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
1046
1104
|
'designbase-figma-usage-status--loading': isLoading,
|
|
1047
1105
|
}, className) },
|
|
1048
1106
|
React.createElement("div", { className: "designbase-figma-usage-status__control" },
|
|
1049
|
-
showUsage && (React.createElement("button", { ref: triggerRef, type: "button", className: "designbase-figma-usage-status__trigger", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": isLoading ? fallbackText(translate, 'loading', '불러오는 중') : triggerLabel, disabled: isLoading, onClick: togglePopover }, isLoading ? (React.createElement("span", { className: "designbase-figma-usage-status__skeleton", "aria-hidden": "true" })) : isPaid ? (React.createElement(
|
|
1107
|
+
showUsage && (React.createElement("button", { ref: triggerRef, type: "button", className: "designbase-figma-usage-status__trigger", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": isLoading ? fallbackText(translate, 'loading', '불러오는 중') : triggerLabel, disabled: isLoading, onClick: togglePopover }, isLoading ? (React.createElement("span", { className: "designbase-figma-usage-status__skeleton", "aria-hidden": "true" })) : isPaid ? (React.createElement(React.Fragment, null,
|
|
1108
|
+
React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
|
|
1109
|
+
React.createElement("span", { style: { width: '100%' } })),
|
|
1110
|
+
React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"))) : hasDailyLimit ? (React.createElement(React.Fragment, null,
|
|
1050
1111
|
React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
|
|
1051
|
-
React.createElement("span", { style: { width: `${
|
|
1112
|
+
React.createElement("span", { style: { width: `${remainingPercent}%` } })),
|
|
1052
1113
|
React.createElement("span", { className: "designbase-figma-usage-status__usage-value" },
|
|
1053
1114
|
React.createElement("strong", null, remaining),
|
|
1054
1115
|
React.createElement("span", null,
|
|
@@ -1057,43 +1118,34 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
1057
1118
|
showPlan && !isLoading && (React.createElement("button", { type: "button", className: "designbase-figma-usage-status__plan-badge", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": planTitle, onClick: togglePopover }, planLabel))),
|
|
1058
1119
|
isOpen && (React.createElement("div", { id: popoverId, className: "designbase-figma-usage-status__popover", role: "dialog", "aria-label": fallbackText(translate, 'usageDetails', '사용량 상세') },
|
|
1059
1120
|
React.createElement("div", { className: "designbase-figma-usage-status__popover-header" },
|
|
1060
|
-
React.createElement("span", { className: "designbase-figma-usage-status__plan-
|
|
1121
|
+
React.createElement("span", { className: "designbase-figma-usage-status__plan-heading" },
|
|
1122
|
+
React.createElement("span", null, fallbackText(translate, 'license.current_plan', '현재 플랜')),
|
|
1123
|
+
React.createElement("strong", null, planTitle)),
|
|
1061
1124
|
!isBeta && (React.createElement(react.Button, { className: "designbase-figma-usage-status__upgrade", variant: isPaid ? 'secondary' : 'primary', size: "s", endIcon: "chevron-right", onPress: handleManageClick }, upgradeLabel))),
|
|
1062
|
-
isPaid ? (React.createElement(
|
|
1063
|
-
React.createElement("
|
|
1064
|
-
|
|
1125
|
+
isPaid ? (React.createElement(React.Fragment, null,
|
|
1126
|
+
React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
|
|
1127
|
+
React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
|
|
1128
|
+
React.createElement("span", null,
|
|
1129
|
+
React.createElement("strong", null, fallbackText(translate, 'unlimitedUsage', '무제한 사용')),
|
|
1130
|
+
React.createElement("small", null, fallbackText(translate, 'unlimitedUsageTooltip', '모든 기능을 제한 없이 사용할 수 있습니다')))),
|
|
1131
|
+
paidMetadata.length > 0 && (React.createElement("dl", { className: "designbase-figma-usage-status__license-meta" }, paidMetadata.map((item) => (React.createElement("div", { key: item.label },
|
|
1132
|
+
React.createElement("dt", null, item.label),
|
|
1133
|
+
React.createElement("dd", null, item.value)))))))) : isBeta ? (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'betaUsageDescription', '베타 기간에는 사용량 제한이 적용되지 않습니다'))) : hasDailyLimit ? (React.createElement("div", { className: "designbase-figma-usage-status__usage-detail" },
|
|
1065
1134
|
React.createElement("div", { className: "designbase-figma-usage-status__usage-summary" },
|
|
1066
1135
|
React.createElement("strong", { className: "designbase-figma-usage-status__usage-count" },
|
|
1067
|
-
|
|
1136
|
+
remaining,
|
|
1068
1137
|
React.createElement("span", { className: "designbase-figma-usage-status__usage-divider" }, "/"),
|
|
1069
1138
|
limit),
|
|
1070
|
-
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(translate, '
|
|
1071
|
-
|
|
1072
|
-
|
|
1139
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(translate, 'remainingToday', '오늘 남은 사용량')),
|
|
1140
|
+
React.createElement("strong", { className: "designbase-figma-usage-status__usage-percent" },
|
|
1141
|
+
remainingPercent,
|
|
1142
|
+
"%")),
|
|
1143
|
+
React.createElement("div", { className: "designbase-figma-usage-status__progress", role: "progressbar", "aria-label": fallbackText(translate, 'remainingToday', '오늘 남은 사용량'), "aria-valuemin": 0, "aria-valuemax": limit, "aria-valuenow": remaining, "aria-valuetext": `${remaining} / ${limit}, ${remainingPercent}%` },
|
|
1144
|
+
React.createElement("span", { style: { width: `${remainingPercent}%` } })),
|
|
1073
1145
|
React.createElement("p", { className: "designbase-figma-usage-status__remaining-copy" }, resolveText(translate, { key: 'remainingCopy', values: { count: remaining } }, `${remaining}회 남음 · 매일 리셋`)))) : (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'usageUnavailable', '사용량 제한이 없습니다')))))));
|
|
1074
1146
|
};
|
|
1075
1147
|
UsageStatusPopover.displayName = 'UsageStatusPopover';
|
|
1076
1148
|
|
|
1077
|
-
const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, isLoading = false, showPaymentStatus = true, showUsageInfo = true, maxDailyUsage = 20, showPaymentBadge = true, showDonation = true, donationUrl = 'https://buymeacoffee.com/designbase', donationText = { key: 'donation.buy_me_a_coffee' }, showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t, children, }) => {
|
|
1078
|
-
const { t: translate } = useFigmaUiI18n(currentLanguage, t);
|
|
1079
|
-
const resolvedShowUsageInfo = showPaymentStatus && showUsageInfo;
|
|
1080
|
-
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
1081
|
-
const resolvedLicensePageClick = paymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
1082
|
-
const hasChildren = React.Children.count(children) > 0;
|
|
1083
|
-
const classes = clsx('designbase-figma-footer', {
|
|
1084
|
-
'designbase-figma-footer--has-children': hasChildren,
|
|
1085
|
-
}, className);
|
|
1086
|
-
return (React.createElement("footer", { className: classes },
|
|
1087
|
-
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
1088
|
-
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
1089
|
-
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: translate })),
|
|
1090
|
-
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
1091
|
-
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: paymentStatus, usageCount: usageCount, maxDailyUsage: maxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: translate })),
|
|
1092
|
-
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: translate })))),
|
|
1093
|
-
children));
|
|
1094
|
-
};
|
|
1095
|
-
FigmaFooter.displayName = 'FigmaFooter';
|
|
1096
|
-
|
|
1097
1149
|
const FigmaToastStack = ({ toasts, position, maxToasts, onRemoveToast, }) => {
|
|
1098
1150
|
const visibleToasts = [...toasts]
|
|
1099
1151
|
.sort((a, b) => b.createdAt - a.createdAt)
|
|
@@ -1154,26 +1206,195 @@ const FigmaToast = ({ message, isVisible, onClose, type = 'info', duration = 300
|
|
|
1154
1206
|
React.createElement(react.Toast, { toastId: "figma-toast-simple", title: message, status: type, duration: duration, showProgress: false, showClose: true, onClose: onClose })));
|
|
1155
1207
|
};
|
|
1156
1208
|
const useFigmaToast = () => {
|
|
1157
|
-
const [toast, setToast] = React.useState({
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1209
|
+
const [toast, setToast] = React.useState({
|
|
1210
|
+
message: '',
|
|
1211
|
+
isVisible: false,
|
|
1212
|
+
type: 'info',
|
|
1213
|
+
});
|
|
1214
|
+
const timeoutRef = React.useRef(null);
|
|
1215
|
+
const showToast = React.useCallback((message, type = 'info') => {
|
|
1216
|
+
if (timeoutRef.current)
|
|
1217
|
+
clearTimeout(timeoutRef.current);
|
|
1218
|
+
setToast({ message, isVisible: true, type });
|
|
1219
|
+
timeoutRef.current = setTimeout(() => {
|
|
1220
|
+
setToast({ message: '', isVisible: false, type: 'info' });
|
|
1221
|
+
timeoutRef.current = null;
|
|
1222
|
+
}, 5000);
|
|
1223
|
+
}, []);
|
|
1224
|
+
React.useEffect(() => () => {
|
|
1225
|
+
if (timeoutRef.current)
|
|
1226
|
+
clearTimeout(timeoutRef.current);
|
|
1161
1227
|
}, []);
|
|
1162
|
-
const toastComponent = toast.isVisible ? (React.createElement(FigmaToast, { message: toast.message, isVisible: toast.isVisible, onClose: () => setToast({ message: '', isVisible: false }), type:
|
|
1228
|
+
const toastComponent = toast.isVisible ? (React.createElement(FigmaToast, { message: toast.message, isVisible: toast.isVisible, onClose: () => setToast({ message: '', isVisible: false, type: 'info' }), type: toast.type, duration: 5000 })) : null;
|
|
1163
1229
|
return { showToast, toastComponent };
|
|
1164
1230
|
};
|
|
1165
1231
|
FigmaToastProvider.displayName = 'FigmaToastProvider';
|
|
1166
1232
|
FigmaToast.displayName = 'FigmaToast';
|
|
1167
1233
|
|
|
1234
|
+
const DEFAULT_LOW_USAGE_WARNING = { key: 'lowUsageWarning' };
|
|
1235
|
+
const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, remainingUsage, listenForUsageMessages = true, isLoading = false, showPaymentStatus = true, showUsageInfo = true, maxDailyUsage = 20, licenseSource, accessScope, expiresAt, lastVerifiedAt, showLowUsageWarning = true, lowUsageThresholdPercent = 10, lowUsageWarningText = DEFAULT_LOW_USAGE_WARNING, onLowUsageWarning, showPaymentBadge = true, showDonation = true, donationUrl = 'https://buymeacoffee.com/designbase', donationText = { key: 'donation.buy_me_a_coffee' }, showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t, children, }) => {
|
|
1236
|
+
const { t: translate } = useFigmaUiI18n(currentLanguage, t);
|
|
1237
|
+
const { showToast, toastComponent } = useFigmaToast();
|
|
1238
|
+
const [liveUsage, setLiveUsage] = React.useState({});
|
|
1239
|
+
const previousRemainingRef = React.useRef(null);
|
|
1240
|
+
const hasWarnedLowUsageRef = React.useRef(false);
|
|
1241
|
+
const effectivePaymentStatus = liveUsage.paymentStatus ?? paymentStatus;
|
|
1242
|
+
const effectiveUsageCount = liveUsage.usageCount ?? usageCount;
|
|
1243
|
+
const effectiveRemainingUsage = liveUsage.remainingUsage ?? remainingUsage;
|
|
1244
|
+
const effectiveMaxDailyUsage = liveUsage.maxDailyUsage ?? maxDailyUsage;
|
|
1245
|
+
const effectiveLicenseSource = liveUsage.licenseSource ?? licenseSource;
|
|
1246
|
+
const effectiveAccessScope = liveUsage.accessScope ?? accessScope;
|
|
1247
|
+
const effectiveExpiresAt = liveUsage.expiresAt ?? expiresAt;
|
|
1248
|
+
const effectiveLastVerifiedAt = liveUsage.lastVerifiedAt ?? lastVerifiedAt;
|
|
1249
|
+
const limit = Math.max(1, Math.round(effectiveMaxDailyUsage));
|
|
1250
|
+
const resolvedRemaining = typeof effectiveRemainingUsage === 'number' && Number.isFinite(effectiveRemainingUsage)
|
|
1251
|
+
? Math.min(limit, Math.max(0, Math.round(effectiveRemainingUsage)))
|
|
1252
|
+
: Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(effectiveUsageCount))));
|
|
1253
|
+
const remainingPercent = Math.round((resolvedRemaining / limit) * 100);
|
|
1254
|
+
const resolvedShowUsageInfo = showPaymentStatus
|
|
1255
|
+
&& (showUsageInfo || effectivePaymentStatus === 'FREE' || effectivePaymentStatus === 'PAID');
|
|
1256
|
+
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
1257
|
+
const resolvedLicensePageClick = effectivePaymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
1258
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
1259
|
+
React.useEffect(() => {
|
|
1260
|
+
if (!listenForUsageMessages || typeof window === 'undefined')
|
|
1261
|
+
return undefined;
|
|
1262
|
+
const handleUsageMessage = (event) => {
|
|
1263
|
+
const message = event.data?.pluginMessage;
|
|
1264
|
+
if (!message || !['initialize', 'update-plugin-status', 'usage-consume-result'].includes(message.type)) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const nextLimit = [message.usageLimit, message.freeDailyLimit, message.maxDailyUsage]
|
|
1268
|
+
.find((value) => typeof value === 'number' && Number.isFinite(value) && value > 0);
|
|
1269
|
+
const nextUsed = [message.usageUsed, message.dailyUsage]
|
|
1270
|
+
.find((value) => typeof value === 'number' && Number.isFinite(value) && value >= 0);
|
|
1271
|
+
const nextRemaining = typeof message.remainingUsage === 'number' && Number.isFinite(message.remainingUsage)
|
|
1272
|
+
? message.remainingUsage
|
|
1273
|
+
: typeof nextLimit === 'number' && typeof nextUsed === 'number'
|
|
1274
|
+
? Math.max(0, nextLimit - nextUsed)
|
|
1275
|
+
: undefined;
|
|
1276
|
+
const nextPaymentStatus = message.paymentStatus === 'PAID'
|
|
1277
|
+
? 'PAID'
|
|
1278
|
+
: message.paymentStatus === 'FREE' || message.paymentStatus === 'UNPAID'
|
|
1279
|
+
? 'FREE'
|
|
1280
|
+
: undefined;
|
|
1281
|
+
const nextLicenseSource = typeof message.licenseSource === 'string' ? message.licenseSource : undefined;
|
|
1282
|
+
const nextAccessScope = typeof message.accessScope === 'string' ? message.accessScope : undefined;
|
|
1283
|
+
const nextExpiresAt = typeof message.expiresAt === 'string' ? message.expiresAt : undefined;
|
|
1284
|
+
const rawLastVerifiedAt = message.lastVerifiedAt;
|
|
1285
|
+
const numericLastVerifiedAt = typeof rawLastVerifiedAt === 'number' && Number.isFinite(rawLastVerifiedAt)
|
|
1286
|
+
? rawLastVerifiedAt < 1000000000000
|
|
1287
|
+
? rawLastVerifiedAt * 1000
|
|
1288
|
+
: rawLastVerifiedAt
|
|
1289
|
+
: undefined;
|
|
1290
|
+
const parsedLastVerifiedAt = numericLastVerifiedAt === undefined
|
|
1291
|
+
? undefined
|
|
1292
|
+
: new Date(numericLastVerifiedAt);
|
|
1293
|
+
const nextLastVerifiedAt = typeof rawLastVerifiedAt === 'string'
|
|
1294
|
+
? rawLastVerifiedAt
|
|
1295
|
+
: parsedLastVerifiedAt && !Number.isNaN(parsedLastVerifiedAt.getTime())
|
|
1296
|
+
? parsedLastVerifiedAt.toISOString()
|
|
1297
|
+
: undefined;
|
|
1298
|
+
if (nextPaymentStatus === undefined
|
|
1299
|
+
&& nextLimit === undefined
|
|
1300
|
+
&& nextUsed === undefined
|
|
1301
|
+
&& nextRemaining === undefined
|
|
1302
|
+
&& nextLicenseSource === undefined
|
|
1303
|
+
&& nextAccessScope === undefined
|
|
1304
|
+
&& nextExpiresAt === undefined
|
|
1305
|
+
&& nextLastVerifiedAt === undefined) {
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
setLiveUsage((previous) => ({
|
|
1309
|
+
...previous,
|
|
1310
|
+
...(nextPaymentStatus ? { paymentStatus: nextPaymentStatus } : {}),
|
|
1311
|
+
...(typeof nextLimit === 'number' ? { maxDailyUsage: nextLimit } : {}),
|
|
1312
|
+
...(typeof nextUsed === 'number' ? { usageCount: nextUsed } : {}),
|
|
1313
|
+
...(typeof nextRemaining === 'number' ? { remainingUsage: nextRemaining } : {}),
|
|
1314
|
+
...(nextLicenseSource ? { licenseSource: nextLicenseSource } : {}),
|
|
1315
|
+
...(nextAccessScope ? { accessScope: nextAccessScope } : {}),
|
|
1316
|
+
...(nextExpiresAt ? { expiresAt: nextExpiresAt } : {}),
|
|
1317
|
+
...(nextLastVerifiedAt ? { lastVerifiedAt: nextLastVerifiedAt } : {}),
|
|
1318
|
+
}));
|
|
1319
|
+
};
|
|
1320
|
+
window.addEventListener('message', handleUsageMessage);
|
|
1321
|
+
return () => window.removeEventListener('message', handleUsageMessage);
|
|
1322
|
+
}, [listenForUsageMessages]);
|
|
1323
|
+
React.useEffect(() => {
|
|
1324
|
+
const previousRemaining = previousRemainingRef.current;
|
|
1325
|
+
const shouldTrack = showLowUsageWarning && effectivePaymentStatus === 'FREE' && effectiveMaxDailyUsage > 0;
|
|
1326
|
+
if (!shouldTrack) {
|
|
1327
|
+
previousRemainingRef.current = resolvedRemaining;
|
|
1328
|
+
hasWarnedLowUsageRef.current = false;
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
if (remainingPercent >= lowUsageThresholdPercent) {
|
|
1332
|
+
hasWarnedLowUsageRef.current = false;
|
|
1333
|
+
}
|
|
1334
|
+
else if (previousRemaining !== null
|
|
1335
|
+
&& resolvedRemaining < previousRemaining
|
|
1336
|
+
&& !hasWarnedLowUsageRef.current) {
|
|
1337
|
+
hasWarnedLowUsageRef.current = true;
|
|
1338
|
+
const warningText = typeof lowUsageWarningText === 'string'
|
|
1339
|
+
? lowUsageWarningText
|
|
1340
|
+
: {
|
|
1341
|
+
...lowUsageWarningText,
|
|
1342
|
+
values: {
|
|
1343
|
+
...(lowUsageWarningText.values || {}),
|
|
1344
|
+
count: resolvedRemaining,
|
|
1345
|
+
percent: remainingPercent,
|
|
1346
|
+
},
|
|
1347
|
+
};
|
|
1348
|
+
onLowUsageWarning?.({
|
|
1349
|
+
remaining: resolvedRemaining,
|
|
1350
|
+
percent: remainingPercent,
|
|
1351
|
+
limit,
|
|
1352
|
+
});
|
|
1353
|
+
showToast(resolveText(translate, warningText, `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`) || `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`, 'warning');
|
|
1354
|
+
}
|
|
1355
|
+
previousRemainingRef.current = resolvedRemaining;
|
|
1356
|
+
}, [
|
|
1357
|
+
lowUsageThresholdPercent,
|
|
1358
|
+
lowUsageWarningText,
|
|
1359
|
+
effectiveMaxDailyUsage,
|
|
1360
|
+
effectivePaymentStatus,
|
|
1361
|
+
onLowUsageWarning,
|
|
1362
|
+
remainingPercent,
|
|
1363
|
+
resolvedRemaining,
|
|
1364
|
+
showLowUsageWarning,
|
|
1365
|
+
showToast,
|
|
1366
|
+
translate,
|
|
1367
|
+
]);
|
|
1368
|
+
const classes = clsx('designbase-figma-footer', {
|
|
1369
|
+
'designbase-figma-footer--has-children': hasChildren,
|
|
1370
|
+
}, className);
|
|
1371
|
+
return (React.createElement("footer", { className: classes },
|
|
1372
|
+
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
1373
|
+
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
1374
|
+
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: translate })),
|
|
1375
|
+
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
1376
|
+
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: effectivePaymentStatus, usageCount: effectiveUsageCount, remainingUsage: effectiveRemainingUsage, maxDailyUsage: effectiveMaxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, licenseSource: effectiveLicenseSource, accessScope: effectiveAccessScope, expiresAt: effectiveExpiresAt, lastVerifiedAt: effectiveLastVerifiedAt, locale: currentLanguage, t: translate })),
|
|
1377
|
+
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: translate })))),
|
|
1378
|
+
children,
|
|
1379
|
+
toastComponent));
|
|
1380
|
+
};
|
|
1381
|
+
FigmaFooter.displayName = 'FigmaFooter';
|
|
1382
|
+
|
|
1168
1383
|
const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = false, value = '', onValueChange, label = { key: 'form.license_key' }, placeholder = { key: 'form.license_placeholder' }, submitText = { key: 'form.submit' }, submittingText = { key: 'form.verifying' }, helperText, className, t, locale, }) => {
|
|
1169
1384
|
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1170
1385
|
const [inputValue, setInputValue] = React.useState(value);
|
|
1171
1386
|
React.useEffect(() => {
|
|
1172
1387
|
setInputValue(value);
|
|
1173
1388
|
}, [value]);
|
|
1389
|
+
const submitLicense = async () => {
|
|
1390
|
+
const normalizedValue = inputValue.trim();
|
|
1391
|
+
if (!normalizedValue || disabled || isSubmitting)
|
|
1392
|
+
return;
|
|
1393
|
+
await onLicenseSubmit(normalizedValue);
|
|
1394
|
+
};
|
|
1174
1395
|
const handleSubmit = async (e) => {
|
|
1175
1396
|
e.preventDefault();
|
|
1176
|
-
await
|
|
1397
|
+
await submitLicense();
|
|
1177
1398
|
};
|
|
1178
1399
|
const handleChange = (value) => {
|
|
1179
1400
|
setInputValue(value);
|
|
@@ -1193,7 +1414,9 @@ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = fals
|
|
|
1193
1414
|
handleChange(value);
|
|
1194
1415
|
}
|
|
1195
1416
|
}, placeholder: displayPlaceholder, helperText: displayHelperText, disabled: disabled || isSubmitting, size: "m" })),
|
|
1196
|
-
React.createElement(react.Button, { type: "
|
|
1417
|
+
React.createElement(react.Button, { type: "button", variant: "primary", size: "m", loading: isSubmitting, disabled: !inputValue.trim() || isSubmitting || disabled, fullWidth: true, onPress: () => {
|
|
1418
|
+
void submitLicense();
|
|
1419
|
+
} }, isSubmitting ? displaySubmittingText : displaySubmitText)));
|
|
1197
1420
|
};
|
|
1198
1421
|
FormWithSubmit.displayName = 'FormWithSubmit';
|
|
1199
1422
|
|
|
@@ -1316,7 +1539,7 @@ function formatSource(value, t) {
|
|
|
1316
1539
|
}
|
|
1317
1540
|
return value;
|
|
1318
1541
|
}
|
|
1319
|
-
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, locale, className, }) => {
|
|
1542
|
+
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, showHeader = true, allowRevealKey = false, t, locale, className, }) => {
|
|
1320
1543
|
const { locale: resolvedLocale, t: translate } = useFigmaUiI18n(locale, t);
|
|
1321
1544
|
const [isKeyRevealed, setIsKeyRevealed] = React.useState(false);
|
|
1322
1545
|
const normalizedLicenseStatus = licenseStatus?.toLowerCase();
|
|
@@ -1327,13 +1550,15 @@ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSour
|
|
|
1327
1550
|
: resolveText(translate, { key: 'license.status_inactive' }, 'License check failed');
|
|
1328
1551
|
const statusClass = isActive ? 'active' : 'inactive';
|
|
1329
1552
|
return (React.createElement("section", { className: `designbase-figma-license-status-card designbase-figma-license-status-card--${statusClass} ${className || ''}`, "aria-label": statusText },
|
|
1330
|
-
React.createElement("div", { className: "designbase-figma-license-status-card__header" },
|
|
1553
|
+
showHeader && (React.createElement("div", { className: "designbase-figma-license-status-card__header" },
|
|
1331
1554
|
React.createElement("div", null,
|
|
1332
1555
|
React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
|
|
1333
1556
|
? resolveText(translate, { key: 'license.activated' }, '라이선스 활성화됨')
|
|
1334
1557
|
: resolveText(translate, { key: 'license.status_label' }, '라이선스 상태')),
|
|
1335
1558
|
React.createElement("h3", null, statusText)),
|
|
1336
|
-
React.createElement("span", { className: "designbase-figma-license-status-card__badge" },
|
|
1559
|
+
React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, isActive
|
|
1560
|
+
? resolveText(translate, { key: 'license.status_active' }, '활성')
|
|
1561
|
+
: resolveText(translate, { key: 'license.status_inactive' }, '비활성')))),
|
|
1337
1562
|
validationMessage && !isActive && (React.createElement("p", { className: "designbase-figma-license-status-card__message" }, validationMessage)),
|
|
1338
1563
|
licenseKey && (React.createElement("div", { className: "designbase-figma-license-status-card__key-row" },
|
|
1339
1564
|
React.createElement("span", null, resolveText(translate, { key: 'license.key_label' }, '라이선스 키')),
|
|
@@ -1728,7 +1953,7 @@ function formatUsageLabel(state, t) {
|
|
|
1728
1953
|
}
|
|
1729
1954
|
return resolveText(t, { key: 'license.usage_generic', values: { remaining, limit } }, `남은 횟수 ${remaining}/${limit}`) ?? `남은 횟수 ${remaining}/${limit}`;
|
|
1730
1955
|
}
|
|
1731
|
-
const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicenseKey = '', wordpressLinks, paymentPageUrl, mode = 'pricing', membershipUrl, enableAccountAuth = true, showLicenseKeyInput = true, pricingFeatures, pricing, paymentType = 'subscription', messageTypes, onStateChange, setPaymentStatus, setUsageCount, onLicenseSubmit, usageCount: initialUsageCount = 0, t, locale, className, }) => {
|
|
1956
|
+
const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicenseKey = '', licenseStatus: initialLicenseStatus, licenseSource: initialLicenseSource, accessScope: initialAccessScope, expiresAt: initialExpiresAt, lastVerifiedAt: initialLastVerifiedAt, wordpressLinks, paymentPageUrl, mode = 'pricing', membershipUrl, enableAccountAuth = true, showLicenseKeyInput = true, pricingFeatures, pricing, paymentType = 'subscription', messageTypes, onStateChange, setPaymentStatus, setUsageCount, onLicenseSubmit, usageCount: initialUsageCount = 0, t, locale, className, }) => {
|
|
1732
1957
|
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1733
1958
|
const links = React.useMemo(() => ({
|
|
1734
1959
|
purchaseUrl: wordpressLinks?.purchaseUrl ?? paymentPageUrl,
|
|
@@ -1741,13 +1966,21 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1741
1966
|
initialState: {
|
|
1742
1967
|
paymentStatus: initialStatus,
|
|
1743
1968
|
licenseKey: initialLicenseKey,
|
|
1969
|
+
hasLicenseKey: Boolean(initialLicenseKey),
|
|
1970
|
+
isLicenseRegistered: initialStatus === 'PAID',
|
|
1971
|
+
planTier: initialStatus === 'PAID' ? 'PRO' : 'FREE',
|
|
1972
|
+
usageLimit: initialStatus === 'PAID' ? 'unlimited' : 0,
|
|
1973
|
+
licenseStatus: initialLicenseStatus,
|
|
1974
|
+
licenseSource: initialLicenseSource,
|
|
1975
|
+
accessScope: initialAccessScope,
|
|
1976
|
+
expiresAt: initialExpiresAt,
|
|
1977
|
+
lastVerifiedAt: initialLastVerifiedAt,
|
|
1744
1978
|
usageCount: initialUsageCount,
|
|
1745
1979
|
},
|
|
1746
1980
|
onStateChange,
|
|
1747
1981
|
onPaymentStatusChange: setPaymentStatus,
|
|
1748
1982
|
onUsageCountChange: setUsageCount,
|
|
1749
1983
|
});
|
|
1750
|
-
const [showDetails, setShowDetails] = React.useState(false);
|
|
1751
1984
|
const [licenseKeyDraft, setLicenseKeyDraft] = React.useState(initialLicenseKey);
|
|
1752
1985
|
const isPaid = state.isLicenseRegistered && state.paymentStatus === 'PAID';
|
|
1753
1986
|
const isMembershipMode = mode === 'youtube-membership';
|
|
@@ -1772,7 +2005,7 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1772
2005
|
: isPaid
|
|
1773
2006
|
? resolveText(translate, { key: 'license.pro_account' }, '프로 계정')
|
|
1774
2007
|
: resolveText(translate, { key: 'license.upgrade_to_pro' }, '프로로 업그레이드')),
|
|
1775
|
-
|
|
2008
|
+
isPaid && (React.createElement("span", { className: "designbase-figma-page-license__tier" }, resolveText(translate, { key: 'license.pro_plan' }, '프로 플랜')))),
|
|
1776
2009
|
React.createElement("p", { className: "designbase-figma-page-license__description" }, isPaid
|
|
1777
2010
|
? resolveText(translate, { key: 'license.all_features_available' }, '모든 기능을 사용할 수 있습니다.')
|
|
1778
2011
|
: isMembershipMode
|
|
@@ -1780,16 +2013,16 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1780
2013
|
: resolveText(translate, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
|
|
1781
2014
|
!isPaid && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
1782
2015
|
!isMembershipMode && (React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t: translate })),
|
|
1783
|
-
state.lastMessage && !state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}`, "aria-live": "polite" }, state.lastMessage)),
|
|
1784
|
-
isPaid && (React.createElement("div", { className: "designbase-figma-page-
|
|
1785
|
-
React.createElement("div", { className: "designbase-figma-page-
|
|
1786
|
-
React.createElement("
|
|
1787
|
-
React.createElement(
|
|
1788
|
-
React.createElement(
|
|
1789
|
-
|
|
1790
|
-
React.createElement(
|
|
1791
|
-
|
|
1792
|
-
React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
2016
|
+
state.lastMessage && state.lastErrorCode && !state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}`, "aria-live": "polite" }, state.lastMessage)),
|
|
2017
|
+
isPaid && (React.createElement("div", { className: "designbase-figma-page-license__paid-summary" },
|
|
2018
|
+
React.createElement("div", { className: "designbase-figma-page-license__paid-hero" },
|
|
2019
|
+
React.createElement("span", { "aria-hidden": "true" }, "\u221E"),
|
|
2020
|
+
React.createElement("div", null,
|
|
2021
|
+
React.createElement("strong", null, resolveText(translate, { key: 'license.usage_unlimited' }, '무제한 사용')),
|
|
2022
|
+
React.createElement("p", null, resolveText(translate, { key: 'license.active_until_expiry' }, '만료일까지 모든 기능을 제한 없이 사용할 수 있습니다.')))),
|
|
2023
|
+
React.createElement(LicenseStatusCard, { status: state.paymentStatus, licenseKey: state.licenseKey, licenseStatus: state.licenseStatus, licenseSource: state.licenseSource, accessScope: state.accessScope, expiresAt: state.expiresAt, lastVerifiedAt: state.lastVerifiedAt, validationMessage: state.validationMessage, allowRevealKey: false, showHeader: false, t: translate }),
|
|
2024
|
+
React.createElement("div", { className: "designbase-figma-page-license__paid-actions" },
|
|
2025
|
+
state.activationLimit > 0 && (React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
1793
2026
|
React.createElement("div", { className: "designbase-figma-page-license__remaining" },
|
|
1794
2027
|
Math.max(0, state.activationLimit - state.activationUsage),
|
|
1795
2028
|
' ',
|
|
@@ -1799,12 +2032,12 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1799
2032
|
React.createElement("div", { className: "designbase-figma-page-license__usage-count" },
|
|
1800
2033
|
state.activationUsage,
|
|
1801
2034
|
"/",
|
|
1802
|
-
state.activationLimit)),
|
|
1803
|
-
React.createElement(
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
2035
|
+
state.activationLimit))),
|
|
2036
|
+
React.createElement(react.Button, { onPress: deactivateLicense, variant: "secondary", size: "m", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
2037
|
+
? resolveText(translate, { key: 'license.deactivating' }, '비활성화 중...')
|
|
2038
|
+
: resolveText(translate, { key: 'license.deactivate' }, '라이선스 비활성화')),
|
|
2039
|
+
state.deactivateMessage && state.deactivateErrorCode && (React.createElement("p", { className: `designbase-figma-page-license__action-feedback designbase-figma-page-license__action-feedback--${resolvePaymentFeedbackVariant(state.deactivateErrorCode)}`, role: "alert", "aria-live": "polite" }, state.deactivateMessage)),
|
|
2040
|
+
React.createElement("p", { className: "designbase-figma-page-license__deactivate-hint" }, resolveText(translate, { key: 'license.deactivate_hint' }, '이 기기에서 라이선스 연결을 해제합니다.'))))),
|
|
1808
2041
|
!isPaid && (React.createElement(React.Fragment, null,
|
|
1809
2042
|
isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t: translate })),
|
|
1810
2043
|
!isMembershipMode && (enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|