Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
invertedEcho committed May 20, 2024
1 parent 9567892 commit ae260c9
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 105 deletions.
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"types": "tsc --noEmit",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
Expand Down
6 changes: 2 additions & 4 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AuthGuard } from '@nestjs/passport';
import * as bcrypt from 'bcrypt';
import { db } from 'src/db';
import { userTable } from 'src/db/schema';
import { AuthService } from './auth.service';
import { AuthService, User } from './auth.service';
import { Public } from './public.decorators';

class RegisterDto {
Expand All @@ -28,9 +28,7 @@ export class AuthController {
@Public()
@UseGuards(AuthGuard('local'))
@Post('login')
//TODO: Fix typing
async login(@Request() req: { user: any }) {
console.log({ user: req.user });
async login(@Request() req: { user: User }) {
return this.authService.login(req.user);
}

Expand Down
9 changes: 5 additions & 4 deletions backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import * as bcrypt from 'bcrypt';
import { findUserByName } from 'src/db/functions/user';

export type User = {
id: string;
userName: string;
id: number;
username: string;
email: string;
};

@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}

async validateUser(username: string, password: string): Promise<any> {
async validateUser(username: string, password: string): Promise<User | null> {
const user = await findUserByName(username);
if (user && (await bcrypt.compare(password, user.password))) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand All @@ -23,7 +24,7 @@ export class AuthService {
}

async login(user: User) {
const payload = { username: user.userName, sub: user.id };
const payload = { username: user.username, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
Expand Down
27 changes: 24 additions & 3 deletions backend/src/db/functions/task.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { eq } from 'drizzle-orm';
import { CreateTask, UpdateTask } from 'src/task.controller';
import { CreateTask, OneOffTask, UpdateTask } from 'src/task.controller';
import { db } from '..';
import { SelectTask, taskTable } from '../schema';
import { SelectTask, assignmentTable, taskTable } from '../schema';

export async function dbGetAllTasks(): Promise<SelectTask[]> {
return await db.select().from(taskTable);
Expand All @@ -20,7 +20,7 @@ export async function dbGetTaskById(taskId: number) {
}
}

export async function dbCreateTask({
export async function dbCreateRecurringTask({
title,
description,
taskGroupId,
Expand Down Expand Up @@ -53,3 +53,24 @@ export async function dbUpdateTask({
throw error;
}
}

export async function dbCreateOneOffTask({
title,
description,
userIds,
}: OneOffTask) {
const tasks = await db
.insert(taskTable)
.values({ title, description })
.returning({ taskId: taskTable.id });
const task = tasks[0];

const hydratedAssignments = userIds.map((userId) => {
return {
taskId: task.taskId,
userId,
};
});

await db.insert(assignmentTable).values(hydratedAssignments);
}
26 changes: 22 additions & 4 deletions backend/src/task.controller.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common';
import { SelectTask } from './db/schema';
import { dbCreateTask, dbGetAllTasks, dbUpdateTask } from './db/functions/task';
import {
dbCreateOneOffTask,
dbCreateRecurringTask,
dbGetAllTasks,
dbUpdateTask,
} from './db/functions/task';

export type CreateTask = {
title: string;
description?: string;
taskGroupId?: number;
};

// TODO: extra type for this feels weird
export type OneOffTask = {
title: string;
description: string;
userIds: number[];
};

// todo: this shouldnt be a seperate type
export type UpdateTask = {
title: string;
Expand All @@ -24,15 +36,21 @@ export class TasksController {
return tasks;
}

@Post()
async createTask(@Body() task: CreateTask) {
@Post('/recurring')
async createRecurringTask(@Body() task: CreateTask) {
console.log({ task });
await dbCreateTask(task);
await dbCreateRecurringTask(task);
}

@Put(':id')
async updateTask(@Param('id') id: string, @Body() task: UpdateTask) {
console.log({ updatedTask: task });
await dbUpdateTask({ ...task, id: Number(id) });
}

@Post('/one-off')
async createOneOffTask(@Body() oneOffTask: OneOffTask) {
console.log(oneOffTask);
await dbCreateOneOffTask(oneOffTask);
}
}
14 changes: 12 additions & 2 deletions frontend/app.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"expo": {
"name": "frontend",
"platforms": [
"web",
"android",
"ios"
],
"name": "WG-Tasks",
"slug": "frontend",
"version": "1.0.0",
"orientation": "portrait",
Expand Down Expand Up @@ -29,6 +34,11 @@
},
"plugins": [
"expo-secure-store"
]
],
"extra": {
"eas": {
"projectId": "4c4ae101-99c9-4338-a54f-3a4c2b35d0fc"
}
}
}
}
18 changes: 18 additions & 0 deletions frontend/eas.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"cli": {
"version": ">= 9.0.7"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {}
},
"submit": {
"production": {}
}
}
16 changes: 8 additions & 8 deletions frontend/src/components/user-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ const UserDropdown = ({ data, selectedUserId, onChange }: Props) => {
const [isFocus, setIsFocus] = useState(false);

return (
<View style={styles.container}>
<View style={dropdownStyles.container}>
<Dropdown
style={[styles.dropdown, isFocus && { borderColor: "blue" }]}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
style={[dropdownStyles.dropdown, isFocus && { borderColor: "blue" }]}
placeholderStyle={dropdownStyles.placeholderStyle}
selectedTextStyle={dropdownStyles.selectedTextStyle}
inputSearchStyle={dropdownStyles.inputSearchStyle}
iconStyle={dropdownStyles.iconStyle}
data={data}
search
maxHeight={300}
Expand All @@ -36,7 +36,7 @@ const UserDropdown = ({ data, selectedUserId, onChange }: Props) => {
}}
renderLeftIcon={() => (
<AntDesign
style={styles.icon}
style={dropdownStyles.icon}
color={isFocus ? "blue" : "black"}
name="user"
size={20}
Expand All @@ -49,7 +49,7 @@ const UserDropdown = ({ data, selectedUserId, onChange }: Props) => {

export default UserDropdown;

const styles = StyleSheet.create({
export const dropdownStyles = StyleSheet.create({
container: {
padding: 0,
},
Expand Down
42 changes: 23 additions & 19 deletions frontend/src/components/user-multi-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,60 @@ import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import { MultiSelect } from "react-native-element-dropdown";
import { Ionicons } from "@expo/vector-icons";

function renderItem(item: { value: string; id: number }) {
type MultiSelectItemProps = {
username: string;
};

function MultiSelectItem({ username }: MultiSelectItemProps) {
return (
<View style={styles.item}>
<Text style={styles.selectedTextStyle}>{item.value}</Text>
<Text style={styles.selectedTextStyle}>{username}</Text>
<Ionicons style={styles.icon} color="black" name="person" size={20} />
</View>
);
}

type Props = {
values: { value: string; id: number }[];
selectedValues: string[];
setSelectedValues: React.Dispatch<React.SetStateAction<string[]>>;
type MultiSelectProps = {
users: { username: string; id: number }[];
selectedUserIds: number[];
setSelectedUserIds: React.Dispatch<React.SetStateAction<number[]>>;
header: string;
};

export default function CustomMultiSelect({
values,
setSelectedValues,
selectedValues,
export default function UserMultiSelect({
users,
setSelectedUserIds: setSelectedValues,
selectedUserIds: selectedValues,
header,
}: Props) {
}: MultiSelectProps) {
return (
<View style={styles.container}>
<View className="py-2">
<Text className="text-white mb-2">{header}</Text>
<MultiSelect
style={styles.dropdown}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={values}
labelField="value"
data={users}
labelField="username"
valueField="id"
placeholder="Select item"
value={selectedValues}
value={selectedValues.map((value) => value.toString())}
activeColor="#9bd4e4"
search
searchPlaceholder="Search..."
onChange={(value) => {
setSelectedValues(value);
onChange={(values) => {
setSelectedValues(values.map((value) => Number(value)));
}}
renderLeftIcon={() => (
<Ionicons style={styles.icon} color="black" name="person" size={20} />
)}
renderItem={renderItem}
renderItem={MultiSelectItem}
renderSelectedItem={(item, unSelect) => (
<TouchableOpacity onPress={() => unSelect && unSelect(item)}>
<View style={styles.selectedStyle}>
<Text style={styles.textSelectedStyle}>{item.value}</Text>
<Text style={styles.textSelectedStyle}>{item.username}</Text>
<Ionicons color="black" name="trash-bin-outline" size={17} />
</View>
</TouchableOpacity>
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/screens/create-task-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Loading from "../components/loading";
import WebDateTimerPicker from "../components/web-date-picker";
import { fetchWrapper } from "../utils/fetchWrapper";
import { getUsers } from "./assignments";
import CustomMultiSelect from "../components/user-multi-select";
import UserMultiSelect from "../components/user-multi-select";

const createTaskGroupSchema = z.object({
title: z.string().min(1, { message: "Title is missing" }),
Expand Down Expand Up @@ -168,10 +168,10 @@ export function CreateTaskGroupScreen() {
<Text className="text-red-300">Interval is required</Text>
)}
</View>
<CustomMultiSelect
values={hydratedUsers}
selectedValues={selectedUserIds}
setSelectedValues={setSelectedUserIds}
<UserMultiSelect
users={users}
selectedUserIds={selectedUserIds}
setSelectedUserIds={setSelectedUserIds}
header="Select Users"
/>
{/* TODO: When inserting a date into the database, it somehow is one day earlier in the database. For example inserting 31.05.2024 -> 30.05.2024 in db
Expand Down
Loading

0 comments on commit ae260c9

Please sign in to comment.