Skip to content

Commit

Permalink
update1
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoshaotongzhi committed Sep 22, 2024
1 parent ee862e3 commit 8edd4e3
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/app/api/restart-challenge/restart-challenge-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { z } from 'zod';

export const restartChallengeSchema = z.object({
userId: z.string().uuid(),
});
38 changes: 38 additions & 0 deletions src/app/api/restart-challenge/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'server-only';
import { NextResponse, type NextRequest } from 'next/server';
import { restartChallengeSchema } from './restart-challenge-schema';
import { serverContainer } from '@/services/server/container';
import { SERVER_SERVICE_KEYS } from '@/services/server/keys';
import { ServerError } from '@/errors/server-error';

export async function PUT(request: NextRequest) {
const auth = serverContainer.get(SERVER_SERVICE_KEYS.Auth);
const userRepository = serverContainer.get(SERVER_SERVICE_KEYS.UserRepository);

try {
const data = await request.json();
const { userId } = restartChallengeSchema.parse(data);

const user = await auth.loadSessionUser();
// impossible that frontend and cookie issue.
if (!user || user.uid !== userId) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 },
);
}
//sign in so no issue in the user id . please improve it!!!!!!
const newTimestamp = await userRepository.restartChallenge(userId);

return NextResponse.json(
{ challengeEndTimestamp: newTimestamp },
{ status: 200 },
);
} catch (e) {
if (e instanceof ServerError) {
return NextResponse.json({ error: e.message }, { status: e.statusCode });
}

return NextResponse.json({ error: 'Bad data.' }, { status: 400 });
}
}
81 changes: 81 additions & 0 deletions src/components/button/button.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// button.module.scss
@use '../../styles/partials/variables/font-families';
@use '../../styles/partials/variables/font-weights';
@use '../../styles/partials/variables/letter-spacings';
@use '../../styles/partials/variables/font-sizes';
@use '../../styles/partials/variables/colors';
@use '../../styles/partials/variables/gradients';

@mixin -base {
position: relative;
display: inline-block;
font-family: font-families.$bebas-neue;
font-weight: font-weights.$regular;
letter-spacing: letter-spacings.$large;
text-align: center;
text-decoration: none;
text-transform: uppercase;
border-style: solid;
border-radius: 40px;
border-width: 4px;
border-color: colors.$black-8by8;
}

@mixin -outline {
&::after {
content: "";
position: absolute;
border-radius: 40px;
top: -8px;
left: -8px;
right: -8px;
bottom: -8px;
border: colors.$white 4px solid;
}
}

@mixin -gradient-text {
:first-child {
// a span element should be added inside the button to contain the text, these styles will apply to it
background: gradients.$yellow-teal;
background-clip: text;
-webkit-background-clip: text;
-moz-background-clip: text;
-webkit-text-fill-color: transparent;
-moz-text-fill-color: transparent;
color: transparent;
font-size: inherit;
}
}

.btn_gradient {
@include -base;
@include -outline;
background: gradients.$yellow-teal;
color: colors.$black-8by8;
}
.custom-font-size {
font-size: 20px; // 你可以根据需要调整字体大小
}
.btn_inverted {
@include -base;
@include -gradient-text;
@include -outline;
background: colors.$black-8by8;
}

.btn_lg {
font-size: font-sizes.$lg;
height: 64px;
border-radius: 40px;
border-width: 4px;
}

.btn_sm {
font-size: font-sizes.$md;
padding: 4px 14px;
}

.btn_wide {
width: 100%;
}
27 changes: 27 additions & 0 deletions src/components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Button.tsx
import React, { FC } from 'react';
import styles from './button.module.scss';

type ButtonProps = {
variant?: 'btn_gradient' | 'btn_inverted';
size?: 'btn_lg' | 'btn_sm';
wide?: boolean;
children: React.ReactNode;
};

const Button: FC<ButtonProps> = ({ variant = 'btn_gradient', size = 'btn_lg', wide = false, children }) => {
const classNames = [styles[variant], styles[size]];
if (wide) {
classNames.push(styles.btn_wide);
}

console.log('Button classNames:', classNames); // Debugging line

return (
<button className={classNames.join(' ')}>
<span>{children}</span>
</button>
);
};

export default Button;
1 change: 1 addition & 0 deletions src/components/button/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Button } from './button';
43 changes: 43 additions & 0 deletions src/contexts/user-context/restart-challenge-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use client";
import { Modal } from "@/components/utils/modal";
import { Button } from "@/components/utils/button";
import { useContextSafely } from "@/hooks/use-context-safely";
import { UserContext } from "@/contexts/user-context";
import { calculateDaysRemaining } from "@/app/progress/calculate-days-remaining";
import { useState } from "react";

export function RestartChallengeModal() {
const [isLoading, setLoading] = useState(false);

const { restartChallenge, user } = useContextSafely(UserContext, "RestartChallengeModal");
if (!user) return null;
const showModal = calculateDaysRemaining(user) === 0;
const restartAndChangeisLoading = async () => {
try{
setLoading(true);
await restartChallenge();
} catch (error) {}
finally {
setLoading(false);
}
}
return (
<Modal ariaLabel="Restart Challenge" theme="dark" isOpen={showModal} closeModal={() => {}}>
{isLoading ? (
<p>Restarting your challenge...</p>
) : (
<>
<p>Oops, times up! But no worries, restart your challenge to continue!</p>

<Button
onClick={restartAndChangeisLoading}
size="sm"
style={{ marginTop: '16px' }} // 你可以根据需要调整这个值
>
Restart Challenge
</Button>
</>
)}
</Modal>
);
}
15 changes: 13 additions & 2 deletions src/services/server/user-repository/supabase-user-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { UserRepository } from './user-repository';
import type { User } from '@/model/types/user';
import type { CreateSupabaseClient } from '../create-supabase-client/create-supabase-client';
import type { IUserRecordParser } from '../user-record-parser/i-user-record-parser';

import { DateTime } from 'luxon';
/**
* An implementation of {@link UserRepository} that interacts with
* a [Supabase](https://supabase.com/) database and parses rows returned from
Expand All @@ -25,7 +25,18 @@ export const SupabaseUserRepository = inject(
private createSupabaseClient: CreateSupabaseClient,
private userRecordParser: IUserRecordParser,
) {}

async restartChallenge(userId: string): Promise<number> {
const supabase = this.createSupabaseClient();
const updatedChallengeEndTimestamp = DateTime.now().plus({ days: 8 }).toUnixInteger();
const { error } = await supabase.from('users').update({ challenge_end_timestamp: updatedChallengeEndTimestamp }).eq('id', userId);

if(error) {
throw new ServerError('Failed to update user.', 500);
}

return updatedChallengeEndTimestamp;
//follow the style of getUserById AND supabase->throw an error
}
async getUserById(userId: string): Promise<User | null> {
const supabase = this.createSupabaseClient();

Expand Down
1 change: 1 addition & 0 deletions src/services/server/user-repository/user-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export interface UserRepository {
makeHybrid(userId: string): Promise<User>;
awardRegisterToVoteBadge(userId: string): Promise<User>;
awardElectionRemindersBadge(userId: string): Promise<User>;
restartChallenge(userId: string): Promise<number>;
}
57 changes: 57 additions & 0 deletions src/stories/components/restart-challenge/restart.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Meta, StoryObj } from '@storybook/react';
import { GlobalStylesProvider } from '@/stories/global-styles-provider';
import { RestartChallengeModal } from '@/contexts/user-context/restart-challenge-modal';
import { Builder } from 'builder-pattern';
import { UserContext,type UserContextType } from '@/contexts/user-context';
import type { User } from '@/model/types/user';
import { PropsWithChildren, useState } from 'react';
import { time } from 'console';
import { promise } from 'zod';
import { DateTime } from 'luxon';
const meta: Meta<typeof RestartChallengeModal> = {
component: RestartChallengeModal,
};

export default meta;

type Story = StoryObj<typeof RestartChallengeModal>;

const createUserContext = () => {
const user: User = Builder<User>().uid("1").challengeEndTimestamp(0).build();
};
function UserContextProvider({ children } : PropsWithChildren) {
const [user, setUser] = useState<User>(Builder<User>().uid("1").challengeEndTimestamp(0).build());
const restartChallenge = async () => {
return new Promise <void >(resolve=>{
setTimeout(() => {
setUser(Builder<User>().uid("1").challengeEndTimestamp(DateTime.now().plus({days: 8}).toMillis()).build());
resolve();
},3000); });
};
return (
<UserContext.Provider value={Builder<UserContextType>()
.user(user)
.restartChallenge(restartChallenge)
.build()}
>
{children}
</UserContext.Provider>
);


}
export const RestartChallenge: Story = {

render: () => {


return (
<GlobalStylesProvider>
<UserContextProvider>
<RestartChallengeModal />
</UserContextProvider>
</GlobalStylesProvider>
);
},
};

0 comments on commit 8edd4e3

Please sign in to comment.