Skip to content

Commit b9ca066

Browse files
authoredMay 12, 2024··
FE: Edit title , Delete title functionality (#47)
* FE: Edit title , Delete title functionality * FE: Minor changes in the conversation page. * Changes in FE and BE , to integrate Conversation * Fix Endpoint to allow , versions * Fix the endpoint for paid modifications * Fix Conversation Edit functionality * Fix ConversationEntity DiscussionEntity * Fix the pipeline start * Fix the pipeline start * Fix the pipeline start * Fix the pipeline paths
1 parent 5fb1dc2 commit b9ca066

27 files changed

+219
-73
lines changed
 

‎.github/workflows/backend.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ jobs:
2727

2828
- name: build with Maven
2929
run: |
30-
./mvnw -B -V -ntp verify
30+
./mvnw -B -V -ntp verify

‎.github/workflows/frontend.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,4 @@ jobs:
7575
- name: build the application
7676
run: |
7777
cd ./frontend
78-
pnpm build
78+
pnpm build

‎backend/src/main/java/com/llm_service/llm_service/config/SecurityConfiguration.java

+17-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.llm_service.llm_service.persistance.entities.Role;
44
import com.llm_service.llm_service.service.jwt.filter.JwtAuthenticationFilter;
55
import com.llm_service.llm_service.service.user.UserDetailsServiceImp;
6+
import java.util.Arrays;
7+
import java.util.List;
68
import lombok.RequiredArgsConstructor;
79
import org.springframework.context.annotation.Bean;
810
import org.springframework.context.annotation.Configuration;
@@ -19,6 +21,7 @@
1921
import org.springframework.security.web.SecurityFilterChain;
2022
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
2123
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
24+
import org.springframework.web.cors.CorsConfiguration;
2225

2326
@Configuration
2427
@EnableWebSecurity
@@ -35,10 +38,21 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
3538

3639
// TODO fix the swagger security config
3740
return http.csrf(AbstractHttpConfigurer::disable)
41+
.cors(cors -> cors.configurationSource(request -> {
42+
CorsConfiguration config = new CorsConfiguration();
43+
config.setAllowedOrigins(List.of("*"));
44+
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
45+
config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
46+
return config;
47+
}))
3848
.authorizeHttpRequests(req -> req.requestMatchers(
39-
"/login/**", "/register/**", "/forget-password/**", "/swagger-ui/**", "/v3/api-docs/**")
49+
"/api/v1/login/**",
50+
"/api/v1/register/**",
51+
"/api/v1/forget-password/**",
52+
"/swagger-ui/**",
53+
"/v3/api-docs/**")
4054
.permitAll()
41-
.requestMatchers("/paid/**")
55+
.requestMatchers("/api/v1/paid/**")
4256
.hasAuthority(Role.PAID.name())
4357
.anyRequest()
4458
.authenticated())
@@ -48,7 +62,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
4862
.exceptionHandling(e -> e.accessDeniedHandler((request, response, accessDeniedException) ->
4963
response.setStatus(HttpStatus.FORBIDDEN.value()))
5064
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
51-
.logout(l -> l.logoutUrl("/logout")
65+
.logout(l -> l.logoutUrl("/api/v1/logout")
5266
.addLogoutHandler(logoutHandler)
5367
.logoutSuccessHandler(
5468
(request, response, authentication) -> SecurityContextHolder.clearContext()))

‎backend/src/main/java/com/llm_service/llm_service/controller/conversation/ConversationController.java

+15-12
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@
1616
import org.springframework.http.ResponseEntity;
1717
import org.springframework.web.bind.annotation.*;
1818

19-
@CrossOrigin("http://localhost:4040")
2019
@RestController
2120
@RequiredArgsConstructor
22-
@RequestMapping("/conversation")
2321
public class ConversationController {
2422

2523
private final ConversationService conversationService;
@@ -34,7 +32,7 @@ public class ConversationController {
3432
content = {@Content(mediaType = "application/json")})
3533
})
3634
@Operation(summary = "Get all conversations")
37-
@GetMapping
35+
@GetMapping(value = "/api/v1/paid/conversation")
3836
public ResponseEntity<List<ConversationResponseCompact>> getAllConversations() throws UnAuthorizedException {
3937
return ResponseEntity.ok(conversationService.getAll().stream()
4038
.map(conversationApiMapper::mapCompact)
@@ -49,7 +47,7 @@ public ResponseEntity<List<ConversationResponseCompact>> getAllConversations() t
4947
content = {@Content(mediaType = "application/json")})
5048
})
5149
@Operation(summary = "Get conversation by ID")
52-
@GetMapping("/{id}")
50+
@GetMapping("/api/v1/conversation/{id}")
5351
public ResponseEntity<ConversationResponse> getConversationById(@PathVariable UUID id)
5452
throws ConversationNotFoundException, UnAuthorizedException {
5553
Conversation conversation =
@@ -66,7 +64,7 @@ public ResponseEntity<ConversationResponse> getConversationById(@PathVariable UU
6664
content = {@Content(mediaType = "application/json")})
6765
})
6866
@Operation(summary = "Create new conversation")
69-
@PostMapping
67+
@PostMapping("/api/v1/conversation")
7068
public ResponseEntity<ConversationResponse> createConversation() throws Exception {
7169
Conversation conversation = conversationService.start();
7270

@@ -81,7 +79,7 @@ public ResponseEntity<ConversationResponse> createConversation() throws Exceptio
8179
content = {@Content(mediaType = "application/json")})
8280
})
8381
@Operation(summary = "Continue conversation using conversation ID")
84-
@PutMapping("/{id}/continue")
82+
@PutMapping("/api/v1/conversation/{id}/continue")
8583
public ResponseEntity<List<DiscussionResponse>> continueConversation(
8684
@PathVariable UUID id, @RequestBody ConversationRequest conversationRequest)
8785
throws ConversationNotFoundException, UnAuthorizedException {
@@ -100,14 +98,19 @@ public ResponseEntity<List<DiscussionResponse>> continueConversation(
10098
content = {@Content(mediaType = "application/json")})
10199
})
102100
@Operation(summary = "update conversation title")
103-
@PutMapping("/{id}")
101+
@PutMapping("/api/v1/conversation/{id}")
104102
public ResponseEntity<ConversationResponseCompact> editConversation(
105103
@PathVariable UUID id, @RequestBody ConversationTitleRequest conversationTitleRequest) throws Exception {
106-
Conversation conversation =
104+
Conversation conversationOld =
107105
conversationService.getByID(id).orElseThrow(() -> new ConversationNotFoundException(id));
108-
conversationService.editTitle(conversation, conversationTitleRequest.getTitle());
106+
Conversation conversationNew =
107+
conversationService.editTitle(conversationOld, conversationTitleRequest.getTitle());
108+
109+
// TODO find why save does not return discussions
110+
// TEMP Solution
111+
conversationNew.setDiscussions(conversationOld.getDiscussions());
109112

110-
return ResponseEntity.status(HttpStatus.OK).body(conversationApiMapper.mapCompact(conversation));
113+
return ResponseEntity.status(HttpStatus.OK).body(conversationApiMapper.mapCompact(conversationNew));
111114
}
112115

113116
@ApiResponses(
@@ -118,7 +121,7 @@ public ResponseEntity<ConversationResponseCompact> editConversation(
118121
content = {@Content(mediaType = "application/json")})
119122
})
120123
@Operation(summary = "deletes a conversation")
121-
@DeleteMapping("/{id}")
124+
@DeleteMapping("/api/v1/conversation/{id}")
122125
public ResponseEntity<Void> deleteConversation(@PathVariable UUID id)
123126
throws ConversationNotFoundException, UnAuthorizedException {
124127
conversationService.getByID(id).orElseThrow(() -> new ConversationNotFoundException(id));
@@ -134,7 +137,7 @@ public ResponseEntity<Void> deleteConversation(@PathVariable UUID id)
134137
content = {@Content(mediaType = "application/json")})
135138
})
136139
@Operation(summary = "deletes all conversations")
137-
@DeleteMapping
140+
@DeleteMapping("/api/v1/conversation")
138141
public ResponseEntity<Void> deleteConversation() {
139142
conversationService.deleteAll();
140143
return ResponseEntity.status(HttpStatus.OK).body(null);

‎backend/src/main/java/com/llm_service/llm_service/controller/user/UserController.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.springframework.security.core.AuthenticationException;
1717
import org.springframework.web.bind.annotation.*;
1818

19-
@CrossOrigin("http://localhost:4040")
2019
@RestController
2120
public class UserController {
2221
private final AuthenticationService authenticationService;
@@ -35,7 +34,7 @@ public UserController(AuthenticationService authenticationService, UserApiMapper
3534
content = {@Content(mediaType = "application/json")})
3635
})
3736
@Operation(summary = "get the current user")
38-
@GetMapping("/me")
37+
@GetMapping("/api/v1/me")
3938
public ResponseEntity<UserResponse> register() throws UnAuthorizedException {
4039
User user = authenticationService.getUser().orElseThrow(UnAuthorizedException::new);
4140
return ResponseEntity.status(HttpStatus.OK).body(userApiMapper.map(user));
@@ -49,7 +48,7 @@ public ResponseEntity<UserResponse> register() throws UnAuthorizedException {
4948
content = {@Content(mediaType = "application/json")})
5049
})
5150
@Operation(summary = "register the user")
52-
@PostMapping("/register")
51+
@PostMapping("/api/v1/register")
5352
public ResponseEntity<UserResponse> register(@RequestBody UserRequest userRequest)
5453
throws UsernameAlreadyExistsException {
5554
User user = authenticationService.register(userRequest);
@@ -64,7 +63,7 @@ public ResponseEntity<UserResponse> register(@RequestBody UserRequest userReques
6463
content = {@Content(mediaType = "application/json")})
6564
})
6665
@Operation(summary = "login phase of the user")
67-
@PostMapping("/login")
66+
@PostMapping("/api/v1/login")
6867
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest loginRequest)
6968
throws UserNotFoundException, AuthenticationException {
7069
AuthenticationResponse authenticationResponse = authenticationService.authenticate(loginRequest);

‎backend/src/main/java/com/llm_service/llm_service/persistance/entities/ConversationEntity.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
import jakarta.persistence.*;
44
import java.util.List;
5-
import lombok.Data;
6-
import lombok.EqualsAndHashCode;
5+
import lombok.*;
76

87
@EqualsAndHashCode(
98
callSuper = true,

‎backend/src/main/java/com/llm_service/llm_service/persistance/entities/DiscussionEntity.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
@Entity
1010
@Table(name = "discussion")
1111
@Data
12-
@Builder
12+
@Builder(toBuilder = true)
1313
@NoArgsConstructor
1414
@AllArgsConstructor
1515
public class DiscussionEntity extends BaseEntity {

‎backend/src/main/java/com/llm_service/llm_service/service/ConversationService.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@ public class ConversationService {
2323
private final DiscussionPersistenceManager discussionPersistenceManager;
2424
private final UserContext userContext;
2525

26-
public Conversation start() throws Exception {
26+
public Conversation start() throws UnAuthorizedException {
2727
Optional<User> user = userContext.getUserFromContext();
2828

2929
if (user.isEmpty()) {
30-
// TODO fix it later
31-
throw new Exception("");
30+
throw new UnAuthorizedException();
3231
}
3332

3433
Conversation conversation = Conversation.builder().discussions(null).build();
@@ -106,15 +105,14 @@ public void deleteAll() {
106105
conversationPersistenceManager.deleteAll();
107106
}
108107

109-
public void editTitle(Conversation conversation, String title) throws Exception {
108+
public Conversation editTitle(Conversation conversation, String title) throws UnAuthorizedException {
110109
Optional<User> user = userContext.getUserFromContext();
111110

112111
if (user.isEmpty()) {
113-
// TODO fix it later
114-
throw new Exception("");
112+
throw new UnAuthorizedException();
115113
}
116114

117-
conversationPersistenceManager.save(
115+
return conversationPersistenceManager.save(
118116
conversation.toBuilder().title(title).build(), user.get());
119117
}
120118

‎frontend/src/hooks/api/constants.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ export const Queries = {
55
User: "User",
66
} as const;
77

8-
const basePath = import.meta.env.VITE_APP_BASE_URL;
8+
const basePath = `${import.meta.env.VITE_APP_BASE_URL}/api/v1`;
99

1010
// Conversation
11-
export const ConversationsPath = `${basePath}/conversation`;
11+
export const ConversationStartPath = `${basePath}/conversation`;
12+
export const ConversationsPath = `${basePath}/paid/conversation`;
1213
export const getConversationPath = (id: ConversationId) => `${basePath}/conversation/${id}`;
1314
export const getContinueConversationPath = (id: ConversationId) => `${getConversationPath(id)}/continue`;
1415

1516
// User
1617
export const UserPath = `${basePath}/me`;
1718
export const LoginPath = `${basePath}/login`;
19+
export const LogoutPath = `${basePath}/logout`;
1820
export const RegisterPath = `${basePath}/register`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useMutation, useQueryClient } from "@tanstack/react-query";
2+
import { ConversationCompact } from "models/Conversation.ts";
3+
import useApi from "hooks/useApi.ts";
4+
import { ConversationId } from "models/Id.ts";
5+
import { getConversationPath, Queries } from "./constants.ts";
6+
7+
export default function useDeleteConversation() {
8+
const callApi = useApi();
9+
const queryClient = useQueryClient();
10+
11+
return useMutation({
12+
mutationFn: async (id: ConversationId) => {
13+
await callApi<void>({
14+
url: getConversationPath(id),
15+
method: "DELETE",
16+
responseAs: "text",
17+
});
18+
},
19+
onSuccess: (_, id) => {
20+
const queryKey = [Queries.Conversation];
21+
queryClient.setQueryData(queryKey, (old: ConversationCompact[]) => old?.filter((item) => item.id !== id));
22+
},
23+
});
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useMutation, useQueryClient } from "@tanstack/react-query";
2+
import { ConversationCompact } from "models/Conversation.ts";
3+
import useApi from "hooks/useApi.ts";
4+
import { ConversationId } from "models/Id.ts";
5+
import { getConversationPath, Queries } from "./constants.ts";
6+
7+
export default function useEditConversation() {
8+
const callApi = useApi();
9+
const queryClient = useQueryClient();
10+
11+
return useMutation<ConversationCompact, unknown, { title: string; id: ConversationId }>({
12+
mutationFn: async ({ title, id }) => {
13+
const conversation = await callApi<ConversationCompact>({
14+
url: getConversationPath(id),
15+
method: "PUT",
16+
body: { title },
17+
});
18+
// TODO add zod validation
19+
20+
return conversation;
21+
},
22+
onSuccess: (conversation) => {
23+
const queryKey = [Queries.Conversation];
24+
queryClient.setQueryData(queryKey, (old: ConversationCompact[]) => {
25+
const conversations = old?.map((item) => {
26+
if (item.id === conversation.id) {
27+
return conversation;
28+
}
29+
return item;
30+
});
31+
32+
return conversations;
33+
});
34+
},
35+
});
36+
}

‎frontend/src/hooks/api/useGetConversation.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ export default function useGetConversation(id: ConversationId) {
1414
// TODO add zod validation
1515
return conversation;
1616
},
17+
staleTime: 5 * 60 * 60,
1718
});
1819
}

‎frontend/src/hooks/api/useGetConversations.ts

+1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ export default function useGetConversations() {
1515
// TODO add zod validation
1616
return conversation;
1717
},
18+
staleTime: 5 * 60 * 60,
1819
});
1920
}

‎frontend/src/hooks/api/useLogout.ts

-9
This file was deleted.
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useMutation } from "@tanstack/react-query";
2+
import useApi from "hooks/useApi.ts";
3+
import { LogoutPath } from "./constants.ts";
4+
5+
export default function useLogoutCall() {
6+
const callApi = useApi();
7+
8+
return useMutation({
9+
mutationFn: () =>
10+
callApi({
11+
method: "POST",
12+
url: LogoutPath,
13+
responseAs: "text",
14+
}),
15+
});
16+
}

‎frontend/src/hooks/api/useStartConversation.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMutation } from "@tanstack/react-query";
22
import useApi from "hooks/useApi.ts";
33
import { Conversation } from "models/Conversation.ts";
4-
import { ConversationsPath } from "./constants.ts";
4+
import { ConversationStartPath } from "./constants.ts";
55

66
type OnSuccessCallback = (conversation: Conversation) => void;
77

@@ -11,7 +11,7 @@ export default function useStartConversation(onSuccessCallback: OnSuccessCallbac
1111
return useMutation<Conversation>({
1212
mutationFn: async () => {
1313
const conversation = await callApi<Conversation>({
14-
url: ConversationsPath,
14+
url: ConversationStartPath,
1515
method: "POST",
1616
});
1717
// TODO add zod validation

‎frontend/src/hooks/useLogout.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import useAuth from "context/useAuth.ts";
2+
import { useLogoutCall } from "./api/useLogoutCall.ts";
3+
4+
export default function useLogout() {
5+
const { setToken } = useAuth();
6+
const { mutateAsync } = useLogoutCall();
7+
8+
return async () => {
9+
await mutateAsync();
10+
setToken(null);
11+
};
12+
}

‎frontend/src/pages/Conversation/Conversation.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function Conversation() {
1414
<Grid
1515
className="h-screen bg-neutral-50 dark:bg-neutral-900"
1616
gridTemplateColumns="20% 80%"
17-
gridTemplateRows="90.30% 9.70%"
17+
gridTemplateRows="85% 15%"
1818
>
1919
<GridItem gridColumn="1" gridRow="1/3" className="bg-neutral-900 ml-2 mr-2">
2020
<Navbar id={id} />

‎frontend/src/pages/Conversation/Navbar/Conversations.tsx

+47-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Button from "ui/Button.tsx";
55
import useGetConversations from "hooks/api/useGetConversations.ts";
66
import { ConversationId } from "models/Id.ts";
77
import { useNavigate } from "react-router-dom";
8+
import useDeleteConversation from "hooks/api/useDeleteConversation.ts";
89
import EditConversationModal from "./EditConversationModal.tsx";
910

1011
interface ConversationProps {
@@ -18,7 +19,27 @@ function Conversations({ id }: ConversationProps) {
1819
};
1920

2021
const { data, isFetching, isError } = useGetConversations();
22+
const { mutateAsync } = useDeleteConversation();
2123
const [modalConversationId, setModalConversationId] = useState("");
24+
25+
const onDelete = async (conversationId: ConversationId) => {
26+
await mutateAsync(conversationId);
27+
const index = data?.findIndex((item) => item.id);
28+
29+
if (!index) return;
30+
31+
if (data?.[index - 1]) {
32+
navigate(`/conversation/${data?.[index - 1].id}`);
33+
return;
34+
}
35+
36+
if (data?.[index + 1]) {
37+
navigate(`/conversation/${data?.[index + 1].id}`);
38+
}
39+
40+
navigate("/conversation");
41+
};
42+
2243
return (
2344
<>
2445
<div
@@ -32,22 +53,36 @@ function Conversations({ id }: ConversationProps) {
3253
{data?.map((conversation) => (
3354
<button
3455
type="button"
35-
className={clsx("truncate border-2 p-2 text-white flex justify-between items-center w-full min-h-8", {
36-
"border-gray-600": conversation.id === id,
37-
})}
56+
className={clsx(
57+
"truncate border-gray-600 border-2 p-2 text-white flex justify-between items-center w-full min-h-6",
58+
{
59+
"border-white": conversation.id === id,
60+
},
61+
)}
3862
key={conversation.id}
3963
onClick={() => onConversationClick(conversation.id)}
4064
>
4165
{conversation.title || "Untitled"}
42-
<Button
43-
onClick={(e) => {
44-
e.stopPropagation();
45-
setModalConversationId(conversation.id);
46-
}}
47-
className="text-white"
48-
>
49-
Edit
50-
</Button>
66+
<div>
67+
<Button
68+
onClick={(e) => {
69+
e.stopPropagation();
70+
setModalConversationId(conversation.id);
71+
}}
72+
className="bg-transparent hover:bg-transparent hover:text-blue-400"
73+
>
74+
Edit
75+
</Button>
76+
<Button
77+
onClick={(e) => {
78+
e.stopPropagation();
79+
onDelete(conversation.id);
80+
}}
81+
className="bg-transparent hover:bg-transparent hover:text-blue-400"
82+
>
83+
Delete
84+
</Button>
85+
</div>
5186
</button>
5287
))}
5388
</div>

‎frontend/src/pages/Conversation/Navbar/EditConversationModal.tsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,30 @@ import Modal from "ui/Modal/Modal.tsx";
22
import InputWithLabel from "ui/InputWithLabel.tsx";
33
import { ConversationId } from "models/Id.ts";
44
import Button from "ui/Button.tsx";
5+
import { useState } from "react";
6+
import useEditConversation from "hooks/api/useEditConversation.ts";
57

68
interface EditModalProps {
79
id: ConversationId;
810
unSetId: () => void;
911
}
1012

1113
function EditConversationModal({ id, unSetId }: EditModalProps) {
14+
const [title, setTitle] = useState("");
15+
const { mutateAsync } = useEditConversation();
16+
17+
const onSubmit = async () => {
18+
await mutateAsync({ title, id });
19+
unSetId();
20+
};
21+
1222
return (
1323
<Modal open={!!id} title="Edit the Converstaion title" onClose={unSetId}>
14-
<InputWithLabel label="New title" />
24+
<InputWithLabel label="New title" value={title} onInput={(e) => setTitle(e.currentTarget.value)} />
1525

16-
<Button onClick={unSetId}>Submit</Button>
26+
<Button disabled={!title} onClick={onSubmit}>
27+
Submit
28+
</Button>
1729
</Modal>
1830
);
1931
}

‎frontend/src/pages/Conversation/Navbar/Navbar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ConversationId } from "models/Id.ts";
33
import { useNavigate } from "react-router-dom";
44
import useGetUser from "hooks/api/useGetUser.ts";
55
import { Roles } from "models/User.ts";
6-
import useLogout from "hooks/api/useLogout.ts";
6+
import useLogout from "hooks/useLogout.ts";
77
import Conversations from "./Conversations.tsx";
88

99
interface NavbarProps {

‎frontend/src/pages/Conversation/Prompt/Prompt.tsx

+3-6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,9 @@ function Prompt({ id }: PromptProps) {
1919
};
2020

2121
return (
22-
<div
23-
className="ml-12 mr-12 h-auto gap-2 flex items-center justify-center bg-neutral-50 dark:bg-neutral-800 border
24-
border-gray-300 dark:border-gray-700 rounded-full"
25-
>
22+
<div className="mx-12 h-full gap-2 flex items-center justify-center bg-neutral-50 dark:bg-neutral-800">
2623
<TextInput
27-
className="flex-1 h-full resize-none bg-transparent text-white rounded-l-full"
24+
className="flex-1 h-full resize-none bg-transparent text-white mb-4"
2825
value={textValue}
2926
onInput={(e) => setTextValue(e.currentTarget.value)}
3027
disabled={isPending}
@@ -33,7 +30,7 @@ function Prompt({ id }: PromptProps) {
3330
<Button
3431
disabled={isPending || !textValue}
3532
onClick={submitQuestion}
36-
className="w-20 bg-blue-500 text-white rounded-full flex items-center justify-center"
33+
className="w-20 bg-blue-500 text-white flex gap-2 items-center justify-center"
3734
>
3835
{isPending && <SpinLoader size="xs" />}
3936
Submit

‎frontend/src/pages/Welcome/Welcome.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Link } from "react-router-dom";
2+
import useAuth from "context/useAuth.ts";
23

34
function Welcome() {
5+
const { token } = useAuth();
6+
47
return (
58
<div className="bg-neutral-50 px-2 py-6 text-center dark:bg-neutral-900 md:px-12 lg:text-left min-h-screen">
69
<div className="w-100 mx-auto sm:max-w-2xl md:max-w-3xl lg:max-w-5xl xl:max-w-7xl">
@@ -34,7 +37,7 @@ function Welcome() {
3437
md:mr-2 md:mb-0"
3538
data-te-ripple-init=""
3639
data-te-ripple-color="light"
37-
to="/login"
40+
to={token ? "/conversation" : "/login"}
3841
role="button"
3942
>
4043
Get started

‎frontend/src/ui/Button.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function Button({ className, ...rest }: ButtonProps) {
88
<button
99
className={twMerge(
1010
"bg-blue-500 p-2 rounded hover:bg-blue-300 disabled:bg-blue-200 disabled:cursor-not-allowed" +
11-
" px-12 pt-4 pb-3.5 text-white",
11+
"px-12 pt-4 pb-3.5 text-white",
1212
className,
1313
)}
1414
type="button"

‎frontend/src/ui/InputWithLabel.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import Input from "./Input.tsx";
33

44
interface InputWithLabelProps extends ComponentProps<"input"> {
55
label: string;
6-
type: "text" | "password";
6+
type?: "text" | "password";
77
}
88

9-
function InputWithLabel({ label, name, ...rest }: InputWithLabelProps) {
9+
function InputWithLabel({ type = "text", label, name, ...rest }: InputWithLabelProps) {
1010
return (
1111
<label htmlFor={label} className="form-label inline-block mb-2 text-gray-700 w-full">
1212
{label}
13-
<Input id={name} name={name} {...rest} />
13+
<Input id={name} type={type} name={name} {...rest} />
1414
</label>
1515
);
1616
}

‎frontend/src/ui/SpinLoader.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default function SpinLoader({ size }: SpinLoaderProps) {
1010
aria-hidden="true"
1111
className={clsx(
1212
{
13-
"w-6 h-6": size === "xs",
13+
"w-4 h-4": size === "xs",
1414
"w-8 h-8": size === "s",
1515
"w-10 h-10": size === "m",
1616
"w-16 h-16": size === "l",

‎frontend/src/ui/TextInput.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ function TextInput({ className, ...rest }: TextInputProps) {
88
return (
99
<textarea
1010
{...rest}
11-
className={clsx("disabled:bg-gray-100 disabled:cursor-not-allowed", twMerge("p-2", className))}
11+
className={clsx(
12+
"disabled:bg-gray-100 disabled:cursor-not-allowed border border-gray-300 dark:border-gray-700",
13+
twMerge("p-2", className),
14+
)}
1215
/>
1316
);
1417
}

0 commit comments

Comments
 (0)
Please sign in to comment.