🎫 feat: Enhance redemption code expiry handling & improve UI responsiveness
Backend • Introduced `validateExpiredTime` helper in `controller/redemption.go`; reused in both Add & Update endpoints to enforce “expiry time must not be earlier than now”, eliminating duplicated checks • Removed `RedemptionCodeStatusExpired` constant and all related references – expiry is now determined exclusively by the `expired_time` field for simpler, safer state management • Simplified `DeleteInvalidRedemptions`: deletes codes that are `used` / `disabled` or `enabled` but already expired, without relying on extra status codes • Controller no longer mutates `status` when listing or fetching redemption codes; clients derive expiry status from timestamp Frontend • Added reusable `isExpired` helper in `RedemptionsTable.js`; leveraged for: – status rendering (orange “Expired” tag) – action-menu enable/disable logic – row styling • Removed duplicated inline expiry logic, improving readability and performance • Adjusted toolbar layout: on small screens the “Clear invalid codes” button now wraps onto its own line, while “Add” & “Copy” remain grouped Result The codebase is now more maintainable, secure, and performant with no redundant constants, centralized validation, and cleaner UI behaviour across devices.
This commit is contained in:
@@ -20,6 +20,8 @@ import {
|
||||
Typography,
|
||||
Card,
|
||||
Tag,
|
||||
Form,
|
||||
DatePicker,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
IconCreditCard,
|
||||
@@ -40,9 +42,10 @@ const EditRedemption = (props) => {
|
||||
name: '',
|
||||
quota: 100000,
|
||||
count: 1,
|
||||
expired_time: 0,
|
||||
};
|
||||
const [inputs, setInputs] = useState(originInputs);
|
||||
const { name, quota, count } = inputs;
|
||||
const { name, quota, count, expired_time } = inputs;
|
||||
|
||||
const handleCancel = () => {
|
||||
props.handleClose();
|
||||
@@ -85,6 +88,9 @@ const EditRedemption = (props) => {
|
||||
localInputs.count = parseInt(localInputs.count);
|
||||
localInputs.quota = parseInt(localInputs.quota);
|
||||
localInputs.name = name;
|
||||
if (localInputs.expired_time === null || localInputs.expired_time === undefined) {
|
||||
localInputs.expired_time = 0;
|
||||
}
|
||||
let res;
|
||||
if (isEdit) {
|
||||
res = await API.put(`/api/redemption/`, {
|
||||
@@ -220,6 +226,25 @@ const EditRedemption = (props) => {
|
||||
required={!isEdit}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Text strong className="block mb-2">{t('过期时间')}</Text>
|
||||
<DatePicker
|
||||
type="dateTime"
|
||||
placeholder={t('选择过期时间(可选,留空为永久)')}
|
||||
showClear
|
||||
value={expired_time ? new Date(expired_time * 1000) : null}
|
||||
onChange={(value) => {
|
||||
if (value === null || value === undefined) {
|
||||
handleInputChange('expired_time', 0);
|
||||
} else {
|
||||
const timestamp = Math.floor(value.getTime() / 1000);
|
||||
handleInputChange('expired_time', timestamp);
|
||||
}
|
||||
}}
|
||||
size="large"
|
||||
className="!rounded-lg w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user