Skip to content

Commit

Permalink
Merge pull request #671 from bounswe/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
mmtftr authored Dec 11, 2024
2 parents 8c293f9 + 786bd78 commit 63276a7
Show file tree
Hide file tree
Showing 32 changed files with 425 additions and 178 deletions.
19 changes: 11 additions & 8 deletions .github/workflows/prod_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,37 @@ jobs:

# Build a Docker image of the application and tag the image with the $GITHUB_SHA.
- name: Build backend image
run: docker build -t registry.digitalocean.com/programming-languages/backend:$(echo $GITHUB_SHA | head -c7) ./backend
run: docker build -t registry.digitalocean.com/programming-languages-2/backend:$(echo $GITHUB_SHA | head -c7) ./backend

- name: Build web image
run: docker build -t registry.digitalocean.com/programming-languages/web:$(echo $GITHUB_SHA | head -c7) ./frontend
run: docker build -t registry.digitalocean.com/programming-languages-2/web:$(echo $GITHUB_SHA | head -c7) ./frontend

- name: Log in to DigitalOcean Container Registry with short-lived credentials
run: doctl registry login --expiry-seconds 1200

# Push the Docker image to registry

- name: Push backend image
run: docker push registry.digitalocean.com/programming-languages/backend:$(echo $GITHUB_SHA | head -c7)
run: docker push registry.digitalocean.com/programming-languages-2/backend:$(echo $GITHUB_SHA | head -c7)

- name: Push web image
run: docker push registry.digitalocean.com/programming-languages/web:$(echo $GITHUB_SHA | head -c7)
run: docker push registry.digitalocean.com/programming-languages-2/web:$(echo $GITHUB_SHA | head -c7)

# tag as latest

- name: Tag backend image
run: docker tag registry.digitalocean.com/programming-languages/backend:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages/backend:latest
run: docker tag registry.digitalocean.com/programming-languages-2/backend:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages-2/backend:latest

- name: Tag web image
run: docker tag registry.digitalocean.com/programming-languages/web:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages/web:latest
run: docker tag registry.digitalocean.com/programming-languages-2/web:$(echo $GITHUB_SHA | head -c7) registry.digitalocean.com/programming-languages-2/web:latest

# Push the Docker image to registry

- name: Push backend image
run: docker push registry.digitalocean.com/programming-languages/backend:latest
run: docker push registry.digitalocean.com/programming-languages-2/backend:latest

- name: Push web image
run: docker push registry.digitalocean.com/programming-languages/web:latest
run: docker push registry.digitalocean.com/programming-languages-2/web:latest

- name: Create deployment again to ensure both images are up to date
run: doctl apps create-deployment 58cc1048-2af2-42f8-a71f-5d1085113742
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,24 +114,24 @@ docker compose build

```bash
# for prod
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages/web:latest
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages/backend:latest
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages-2/web:latest
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages-2/backend:latest

# for staging
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages/web:staging
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages/backend:staging
docker tag bounswe2024group1-451-web:latest registry.digitalocean.com/programming-languages-2/web:staging
docker tag bounswe2024group1-451-backend:latest registry.digitalocean.com/programming-languages-2/backend:staging
```

3. Push images to the registry.

```bash
# for prod
docker push registry.digitalocean.com/programming-languages/web:latest
docker push registry.digitalocean.com/programming-languages/backend:latest
docker push registry.digitalocean.com/programming-languages-2/web:latest
docker push registry.digitalocean.com/programming-languages-2/backend:latest

# for staging
docker push registry.digitalocean.com/programming-languages/web:staging
docker push registry.digitalocean.com/programming-languages/backend:staging
docker push registry.digitalocean.com/programming-languages-2/web:staging
docker push registry.digitalocean.com/programming-languages-2/backend:staging
```

This will trigger a deployment on the DigitalOcean backend.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.group1.programminglanguagesforum.Exceptions.UnauthorizedAccessException;
import com.group1.programminglanguagesforum.Services.TagService;
import com.group1.programminglanguagesforum.Services.UserContextService;
import com.group1.programminglanguagesforum.Services.UserService;
import com.group1.programminglanguagesforum.Util.ApiResponseBuilder;

import jakarta.persistence.EntityExistsException;
Expand All @@ -26,6 +27,7 @@

import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Optional;


@RestController
Expand All @@ -35,6 +37,7 @@ public class TagController extends BaseController {

private final TagService tagService;
private final UserContextService userContextService;
private final UserService userService;

@GetMapping(value = EndpointConstants.TagEndpoints.SEARCH)
public ResponseEntity<GenericApiResponse<TagSearchResponseDto>> tagSearch(
Expand Down Expand Up @@ -94,6 +97,13 @@ public ResponseEntity<GenericApiResponse<GetTagDetailsResponseDto>> getTagDetail
@PostMapping(value = EndpointConstants.TagEndpoints.BASE_PATH)
public ResponseEntity<GenericApiResponse<GetTagDetailsResponseDto>> createTag(@RequestBody CreateTagRequestDto dto){
try{
User user = userContextService.getCurrentUser();
if(userService.calculateReputation(user) < 50){
return ExceptionResponseHandler.IllegalArgumentException(
new IllegalArgumentException("User does not have enough reputation to create a tag which should be at least 50")
);
}

GetTagDetailsResponseDto tagDetails = tagService.createTag(dto);
GenericApiResponse<GetTagDetailsResponseDto> response = GenericApiResponse.<GetTagDetailsResponseDto>builder()
.status(201)
Expand All @@ -115,6 +125,8 @@ public ResponseEntity<GenericApiResponse<GetTagDetailsResponseDto>> createTag(@R
.build();
ApiResponseBuilder.buildErrorResponse(GetTagDetailsResponseDto.class, "Invalid tag type", 400, errorResponse);
return buildResponse(response, HttpStatus.BAD_REQUEST);
} catch (UnauthorizedAccessException e) {
return ExceptionResponseHandler.UnauthorizedAccessException(e);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
@Repository
public interface TagRepository extends JpaRepository<Tag,Long> {
List<Tag> findAllByIdIn(List<Long> ids);
@Query("SELECT t FROM Tag t " +
"JOIN Question q ON t MEMBER OF q.tags " +
"GROUP BY t.id " +
"ORDER BY COUNT(q.id) DESC")

Page<Tag> findTagsByTagNameContainingIgnoreCase(String tagName, Pageable pageable);
@Query("SELECT t FROM Tag t JOIN t.followers u WHERE u.id = :userId")
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/AnswerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const AnswerCard: React.FC<AnswerCardProps> = ({
author,
}) => {
return (
<Card className="border-none bg-neutral-150 px-6 py-8 shadow-sm">
<Card className="border-none #e5e5e5 px-6 py-8 shadow-sm">
<div className="flex flex-col gap-6">
<h3 className="line-clamp-2 text-xl font-semibold text-gray-800">
{title}
Expand All @@ -49,7 +49,7 @@ export const AnswerCard: React.FC<AnswerCardProps> = ({
<Link to={`/users/${author.id}`} className="h-10 w-10">
<img
src={author.profilePicture}
alt={author.name}
alt={"Profile picture"}
className="rounded-full object-cover"
/>
</Link>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/AnswerItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const AnswerItem: React.FC<AnswerItemProps> = ({
const { token } = useAuthStore();

return (
<Card className="border-none bg-neutral-150 px-6 py-8 shadow-sm">
<Card className="border-none #e5e5e5 px-6 py-8 shadow-sm">
<div className="flex flex-col gap-4">
<ContentWithSnippets content={answer.content} />
<div className="flex items-center justify-between">
Expand Down Expand Up @@ -64,7 +64,7 @@ export const AnswerItem: React.FC<AnswerItemProps> = ({
answer.author?.profilePicture ||
"https://placehold.co/100x100"
}
alt={answer.author?.name}
alt={"Profile picture"}
className="h-8 w-8 rounded-full object-cover"
/>
<span className="text-sm font-medium">{answer.author?.name}</span>
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/CodeSnippet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useExecuteCode } from "@/services/api/programmingForumComponents";
import { CodeExecution } from "@/services/api/programmingForumSchemas";
import React from "react";
import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
import { docco } from "react-syntax-highlighter/dist/esm/styles/hljs";
import { a11yLight } from "react-syntax-highlighter/dist/esm/styles/hljs";

interface CodeSnippetProps {
code: string;
Expand Down Expand Up @@ -37,15 +37,15 @@ export const CodeSnippet: React.FC<CodeSnippetProps> = ({ code, language }) => {

return (
<div className="not-prose relative flex flex-col gap-2 overflow-hidden rounded-xl border border-gray-400 bg-gray-100 p-4">
<h4 className="text-sm font-bold">
<h1 className="text-sm font-bold">
{languageUserFriendlyName[
language.replace("-exec", "") as keyof typeof languageUserFriendlyName
] ?? "Unknown"}{" "}
Code Snippet
</h4>
</h1>
<SyntaxHighlighter
language={language.replace("-exec", "")}
style={docco}
style={a11yLight}
PreTag={({ children, ...rest }) => {
return (
<div {...rest}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DifficultyBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const DifficultyBar: React.FC<DifficultyBarProps> = ({
{/* Difficulty Bar */}
<div>
<h2 className="mb-2 text-lg font-semibold">
The community finds this question <strong>{getHighestVotedDifficulty()}</strong> difficulty.
The community finds this question: <strong>{getHighestVotedDifficulty()}</strong> difficulty.
</h2>
<div className="text-sm text-gray-600 mb-2">
<span>Easy: {localCounts.easy} votes</span>,{" "}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DifficultyFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function DifficultyFilter({ value, onChange }: DifficultyFilterProps) {
onChange(val === "all" ? undefined : (val as DifficultyLevel))
}
>
<SelectTrigger className="w-[180px]">
<SelectTrigger className="w-[180px]" aria-label="Filter questions by difficulty">
<SelectValue placeholder="Filter by difficulty" />
</SelectTrigger>
<SelectContent>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/ExerciseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const ExerciseCard: React.FC<ExerciseCardProps> = ({
link,
}) => {
return (
<Card className="flex flex-1 border-none bg-neutral-150 px-6 py-8 shadow-sm">
<Card className="flex flex-1 border-none #e5e5e5 px-6 py-8 shadow-sm">
<div className="flex flex-col gap-6">
<h3 className="line-clamp-2 text-xl font-semibold text-gray-800">
{title}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/HighlightedQuestionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const HighlightedQuestionCard: React.FC<Partial<QuestionSummary>> = ({
)}
<Link
to={`/question/${id}`}
className="flex items-center text-sm font-medium text-gray-800 hover:underline"
className="flex items-center text-sm font-medium text-gray-800 hover:underline p-2"
>
Go to question
<ArrowRight className="ml-1 h-4 w-4" />
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/QuestionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const QuestionCard = React.forwardRef<HTMLDivElement, QuestionCardProps>(
({ id, title, content, votes, answerCount, author, difficulty }, ref) => {
return (
<Card
className="flex flex-1 border-none bg-neutral-150 px-6 py-8 shadow-sm"
className="flex flex-1 border-none #e5e5e5 px-6 py-8 shadow-sm"
ref={ref}
>
<div className="flex flex-col gap-6">
Expand Down Expand Up @@ -65,7 +65,7 @@ export const QuestionCard = React.forwardRef<HTMLDivElement, QuestionCardProps>(
)}
<Link
to={`/question/${id}`}
className="flex items-center text-sm font-medium text-gray-800 hover:underline"
className="flex items-center text-sm font-medium text-gray-800 hover:underline p-2"
>
Go to question
<ArrowRight className="ml-1 h-4 w-4" />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const Tag = ({
)}
<Link
to={`/tag/${tagId}`}
className="text-sm font-medium text-blue-500 hover:underline"
className="text-sm font-medium text-blue-700 hover:underline"
>
See all questions →
</Link>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/TagCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ export const TagCard = React.forwardRef<HTMLDivElement, TagCardProps>(
({ tag }, ref) => {
return (
<Card
className="flex-1 border-none bg-neutral-150 px-6 py-8 shadow-sm"
className="flex-1 border-none #e5e5e5 px-6 py-8 shadow-sm"
ref={ref}
>
<div className="flex flex-col gap-6">
<h3 className="line-clamp-2 text-xl font-semibold text-gray-800">
<h1 className="line-clamp-2 text-xl font-semibold text-gray-800">
{tag.name}
</h3>
</h1>
<div className="flex items-start gap-2">
<p className="line-clamp-3 text-sm font-light text-gray-800">
{tag.description}
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/routes/create-question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -282,15 +282,19 @@ export default function QuestionCreationPage() {
name="difficulty"
render={({ field }) => (
<FormItem>
<label htmlFor="difficulty-select" className="font-semibold mb-2 block">
Difficulty Level
</label>
<FormControl>
<select
id="difficulty-select"
className="rounded border border-gray-300 px-4 py-2"
value={field.value}
onChange={(e) => field.onChange(e.target.value)}
>
<option value="EASY">Easy</option>
<option value="MEDIUM">Medium</option>
<option value="HARD">Hard</option>
<option value="HARD">Hard</option>
</select>
</FormControl>
<FormMessage />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export const Feed = () => {
<section className="mb-12">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold">Tags</h2>
<a href="/tags" className="text-red-500 hover:underline">
<a href="/tags" className="text-red-700 hover:underline">
See all →
</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export default function Profile() {
size="icon"
className="rounded-full bg-red-500 text-white"
>
<Link to="/questions/new">
<Link to="/questions/new" aria-label="Create New Question">
<Plus />
</Link>
</Button>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/routes/question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export default function QuestionPage() {
src={
question.author.profilePicture || "https://placehold.co/640x640"
}
alt={question.author.name + " profile picture"}
alt={"Profile picture"}
className="h-8 w-8 rounded-full object-cover"
/>
<span className="font-semibold">{question.author.name}</span>
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/routes/tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export default function TagPage() {
{tag.logoImage && (
<img
src={tag?.logoImage || "https://placehold.co/400x300"}
alt={`The logo image of ${tag.name}`}
alt={`${tag.name} logo`}
title={`alt:The logo image of ${tag.name}`}
className="h-48 w-full rounded-3xl object-contain lg:h-96"
/>
Expand Down Expand Up @@ -161,6 +161,7 @@ export default function TagPage() {
to={tag.stackExchangeTag}
className="flex items-center gap-2 text-sm font-semibold text-gray-500"
target="_blank"
aria-label={`Visit Stack Exchange for ${tag.name}`}
>
Stack Exchange
</Link>
Expand All @@ -177,15 +178,16 @@ export default function TagPage() {
<div className="mt-4 flex flex-col gap-4 px-4 py-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<h3>Questions</h3>
<h1>Questions</h1>
{!!token && (
<Button
asChild
size="icon"
className="rounded-full bg-red-500 text-white"
className="rounded-full bg-red-700 text-white"
aria-label="Create a new question"
>
<Link to={`/questions/new?tagIds=${tag.tagId}`}>
<Plus />
<Plus aria-hidden="true" />
</Link>
</Button>
)}
Expand Down
5 changes: 4 additions & 1 deletion mobile/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ScrollView, Text } from "react-native";

import { Button, ButtonText, VStack } from "@/components/ui";
import { Button, ButtonText, Divider, VStack } from "@/components/ui";
import useAuthStore from "@/services/auth";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { Feed } from "@/components/Feed";

export default function HomeScreen() {
const auth = useAuthStore();
Expand Down Expand Up @@ -45,6 +46,8 @@ export default function HomeScreen() {
<ButtonText>Logout</ButtonText>
</Button>
)}
<Divider className="my-8" />
<Feed />
</VStack>
</ScrollView>
);
Expand Down
Loading

0 comments on commit 63276a7

Please sign in to comment.