Skip to content

add plan activity config#6139

Merged
c121914yu merged 21 commits intolabring:mainfrom
newfish-cmyk:activity
Dec 24, 2025
Merged

add plan activity config#6139
c121914yu merged 21 commits intolabring:mainfrom
newfish-cmyk:activity

Conversation

@newfish-cmyk
Copy link
Copy Markdown
Contributor

No description provided.

@gru-agent
Copy link
Copy Markdown
Contributor

gru-agent bot commented Dec 23, 2025

TestGru Assignment

Summary

Link CommitId Status Reason
Detail fb3b198 🚫 Skipped No files need to be tested {"packages/global/common/system/config/constants.ts":"File path does not match include patterns.","packages/global/support/wallet/sub/type.d.ts":"File path does not match include patterns.","packages/web/common/file/hooks/useUploadAvatar.tsx":"File path does not match include patterns.","packages/web/components/common/Icon/icons/price/right.svg":"File path does not match include patterns.","packages/web/i18n/en/common.json":"File path does not match include patterns.","packages/web/i18n/zh-CN/common.json":"File path does not match include patterns.","packages/web/i18n/zh-Hant/common.json":"File path does not match include patterns.","projects/app/public/imgs/system/extraSnowflake1.svg":"File path does not match include patterns.","projects/app/public/imgs/system/extraSnowflake2.svg":"File path does not match include patterns.","projects/app/public/imgs/system/extraSnowflake3.svg":"File path does not match include patterns.","projects/app/public/imgs/system/ri…

History Assignment

Tip

You can @gru-agent and leave your feedback. TestGru will make adjustments based on your input

@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 23, 2025

Preview mcp_server Image:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:fatsgpt_mcp_server_c1618c8e8d12f0f71aa7902e44f3f3be6e68085b

@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 23, 2025

Preview sandbox Image:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:fatsgpt_sandbox_c1618c8e8d12f0f71aa7902e44f3f3be6e68085b

@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 23, 2025

Preview fastgpt Image:

registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-pr:fatsgpt_c1618c8e8d12f0f71aa7902e44f3f3be6e68085b

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a promotional activity configuration system for subscription plans, including a new activity advertisement modal, visual enhancements with winter/holiday theming (snowflake decorations), and support for activity-based bonus points.

Key Changes

  • New activity ad modal component with localStorage-based dismissal tracking (24-hour cooldown)
  • Default subscription mode changed from monthly to yearly
  • Activity expiration time display and bonus points visualization across pricing components

Reviewed changes

Copilot reviewed 11 out of 21 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
projects/app/src/web/common/system/api.ts Added new API endpoint getActivityAd for fetching activity advertisements
projects/app/src/pageComponents/price/Standard.tsx Added activity expiration banner, snowflake decorations, bonus points display, and changed default subscription mode to yearly
projects/app/src/pageComponents/price/ExtraPlan.tsx Added activity-themed decorations and bonus points badges to extra plan packages
projects/app/src/components/support/wallet/StandardPlanContentList.tsx Added strike-through pricing and bonus points display for annual plans during activities
projects/app/src/components/support/activity/ActivityAdModal.tsx New modal component for displaying activity promotions with localStorage-based dismissal logic
projects/app/src/components/Layout/index.tsx Integrated ActivityAdModal into main layout
projects/app/public/imgs/system/*.svg Added 7 new SVG files for snowflake and ribbon decorative elements
packages/web/i18n/*/common.json Added translation keys for activity ad modal in English, Simplified Chinese, and Traditional Chinese
packages/web/components/common/Icon/icons/price/right.svg Modified SVG to support dynamic fill color instead of hardcoded color
packages/web/common/file/hooks/useUploadAvatar.tsx Enhanced hook to accept configurable image compression parameters
packages/global/support/wallet/sub/type.d.ts Added annualBonusPoints and activityBonusPoints fields for promotional features
packages/global/common/system/config/constants.ts Added activityAd configuration type
Comments suppressed due to low confidence (1)

packages/web/common/file/hooks/useUploadAvatar.tsx:61

  • Missing dependencies in the handleUploadAvatar callback. The dependency array on line 61 is missing maxW, maxH, and maxSize which are used in the function body on lines 45-47. This could lead to stale closures where the callback uses outdated values if these parameters change.
  const handleUploadAvatar = useCallback(
    async (file: File) => {
      if (!file.name.match(/\.(jpg|png|jpeg)$/)) {
        toast({ title: t('account_info:avatar_can_only_select_jpg_png'), status: 'warning' });
        return;
      }

      startUpload(async () => {
        const compressed = base64ToFile(
          await compressBase64Img({
            base64Img: await fileToBase64(file),
            maxW,
            maxH,
            maxSize
          }),
          file.name
        );
        const { url, fields } = await api({ filename: file.name });
        const formData = new FormData();
        Object.entries(fields).forEach(([k, v]) => formData.set(k, v));
        formData.set('file', compressed);
        const res = await fetch(url, { method: 'POST', body: formData }); // 204
        if (res.ok && res.status === 204) {
          onSuccess?.(`${imageBaseUrl}${fields.key}`);
        }
      });
    },
    [t, toast, api, onSuccess]

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread projects/app/src/components/support/activity/ActivityAdModal.tsx Outdated
Comment thread projects/app/src/components/support/activity/ActivityAdModal.tsx Outdated
Comment thread projects/app/src/pageComponents/price/Standard.tsx Outdated
Comment thread projects/app/src/pageComponents/price/Standard.tsx Outdated
Comment thread projects/app/src/pageComponents/price/ExtraPlan.tsx Outdated
Comment thread projects/app/src/pageComponents/price/Standard.tsx
c121914yu
c121914yu previously approved these changes Dec 23, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 47 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/web/i18n/zh-CN/account_team.json Outdated
Comment thread projects/app/src/pageComponents/price/utils.ts
Comment thread projects/app/src/pageComponents/price/Standard.tsx Outdated
Comment thread projects/app/src/components/support/activity/ActivityAdModal.tsx Outdated
Comment thread projects/app/src/components/support/activity/ActivityAdModal.tsx
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 51 changed files in this pull request and generated 18 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread projects/app/src/web/support/user/auth.ts Outdated
Comment thread packages/web/common/file/hooks/useUploadAvatar.tsx
Comment thread projects/app/src/pageComponents/price/Standard.tsx
Comment thread projects/app/src/components/support/activity/ActivityAdModal.tsx
Comment thread projects/app/src/components/support/activity/ActivityAdModal.tsx
Comment thread packages/global/openapi/admin/support/user/inform/api.ts
Comment thread projects/app/src/web/support/user/inform/api.ts
Comment thread packages/web/i18n/en/common.json Outdated
Comment thread projects/app/src/components/support/activity/ActivityAdModal.tsx
Comment thread projects/app/src/web/support/user/auth.ts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 40 out of 52 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +53 to +69
useEffect(() => {
const calculateQRSize = () => {
const windowHeight = window.innerHeight;
const reservedSpace = 470 + (tip ? 60 : 0) + (discountCouponName ? 30 : 0);
const availableHeight = windowHeight - reservedSpace;

const newSize = Math.min(QR_CODE_SIZE, Math.max(MIN_QR_SIZE, availableHeight));

setDynamicQRSize(newSize);
};

window.addEventListener('resize', calculateQRSize);

return () => {
window.removeEventListener('resize', calculateQRSize);
};
}, [tip, discountCouponName]);
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calculateQRSize function is only called once during the initial mount when the resize event listener is added, but it's never called initially to set the correct size. This means the QR code will always start with the default QR_CODE_SIZE regardless of the current window size. You should call calculateQRSize() immediately after defining it or before adding the event listener.

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +68
const { data } = useRequest2(
async () => {
if (!feConfigs?.isPlus || !userInfo) return;
return getActivityAd();
},
{
manual: false,
onSuccess(res) {
const shouldShowAd = (() => {
if (!res?.id) return false;
if (!closedData) return true;

try {
const { timestamp, adId } = JSON.parse(closedData) as {
timestamp: number;
adId: string;
};
// 不同的广告 id,一定展示
if (adId && res.id !== adId) return true;
const now = Date.now();
// Show if 24 hours passed
return now - timestamp > CLOSED_AD_DURATION;
} catch {
return true;
}
})();

if (res?.activityAdImage && shouldShowAd) {
onOpen();
}
},
refreshDeps: [userInfo]
}
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refreshDeps for useRequest2 is set to [userInfo], but the request also depends on feConfigs?.isPlus. If feConfigs changes after initial mount, the request won't re-run. Consider adding feConfigs?.isPlus to the refreshDeps array, or make the hook manual: true and trigger it manually when both conditions are met.

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +73
z.enum(StandardSubLevelEnum),
TeamStandardSubPlanItemSchema
);
export type StandSubPlanLevelMapType = z.infer<typeof StandSubPlanLevelMapSchema>;

export const PointsPackageItemSchema = z.object({
points: z.int(),
month: z.int(),
price: z.number(),
activityBonusPoints: z.int().optional() // 活动赠送积分
});
export type PointsPackageItem = z.infer<typeof PointsPackageItemSchema>;

export const SubPlanSchema = z.object({
[SubTypeEnum.standard]: StandSubPlanLevelMapSchema.optional(),
[SubTypeEnum.extraDatasetSize]: z.object({ price: z.number() }).optional(),
[SubTypeEnum.extraPoints]: z.object({ packages: PointsPackageItemSchema.array() }).optional(),
planDescriptionUrl: z.string().optional(),
appRegistrationUrl: z.string().optional(),
communitySupportTip: z.string().optional(),
activityExpirationTime: z.date().optional()
});
export type SubPlanType = z.infer<typeof SubPlanSchema>;

export const TeamSubSchema = z.object({
_id: ObjectIdSchema,
teamId: ObjectIdSchema,
type: z.enum(SubTypeEnum),
startTime: z.date(),
expiredTime: z.date(),

currentMode: z.enum(SubModeEnum),
nextMode: z.enum(SubModeEnum),
currentSubLevel: z.enum(StandardSubLevelEnum),
nextSubLevel: z.enum(StandardSubLevelEnum),
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The z.enum() function expects a tuple of string literals, not a TypeScript enum. Since StandardSubLevelEnum, SubModeEnum, and SubTypeEnum are TypeScript enums (as defined in constants.ts), you should use z.nativeEnum() instead of z.enum(). This applies to lines 39, 66, 70-73 where these enums are used with z.enum().

Copilot uses AI. Check for mistakes.
@c121914yu c121914yu merged commit 4fbe27f into labring:main Dec 24, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants