From 5c10232d40e7727fe1aab6560a67fa1dad62356f Mon Sep 17 00:00:00 2001 From: Ahmet Emre Safak Date: Fri, 17 Nov 2023 16:24:03 +0300 Subject: [PATCH 001/246] create components for ProfileCard --- .../predictionpolls/ui/profile/ProfileCard.kt | 248 ++++++++++++++++++ .../app/src/main/res/drawable/ic_add.xml | 13 + .../app/src/main/res/drawable/ic_edit.xml | 13 + 3 files changed, 274 insertions(+) create mode 100644 prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt create mode 100644 prediction-polls/android/app/src/main/res/drawable/ic_add.xml create mode 100644 prediction-polls/android/app/src/main/res/drawable/ic_edit.xml diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt new file mode 100644 index 00000000..87e3bd34 --- /dev/null +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt @@ -0,0 +1,248 @@ +package com.bounswe.predictionpolls.ui.profile + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import com.bounswe.predictionpolls.R +import com.bounswe.predictionpolls.ui.theme.MontserratFontFamily +import com.bounswe.predictionpolls.ui.theme.PredictionPollsTheme + +@Composable +fun ProfileCard(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .clip(MaterialTheme.shapes.medium) + .background(MaterialTheme.colorScheme.primary) + .padding(16.dp) + .wrapContentHeight() + .fillMaxWidth() + ) { + + } +} + +@Composable +private fun ProfileEditButton(onProfileEditPressed: () -> Unit, modifier: Modifier = Modifier) { + Box( + modifier = modifier + .clickable(onClick = onProfileEditPressed) + .background(MaterialTheme.colorScheme.primary) + ) { + Row( + modifier = Modifier + .padding(16.dp) + .align(Alignment.Center), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.ic_edit), + contentDescription = "Edit Profile", + tint = MaterialTheme.colorScheme.onPrimary + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = "Edit Profile", + color = MaterialTheme.colorScheme.onPrimary, + fontWeight = FontWeight.Normal, + fontSize = 20.sp, + letterSpacing = 1.5.sp + ) + } + } +} + +@Composable +private fun CoverPhoto(imageUri: String, modifier: Modifier) { + AsyncImage( + model = imageUri, + contentDescription = "User Badge", + modifier = modifier, + contentScale = ContentScale.Crop, + alignment = Alignment.Center + ) +} + +@Composable +private fun RequestsButton(onRequestsClicked: () -> Unit, modifier: Modifier = Modifier) { + Box( + modifier = modifier + .clickable(onClick = onRequestsClicked) + .background(MaterialTheme.colorScheme.onPrimary) + ) { + Row( + modifier = Modifier + .padding(16.dp) + .align(Alignment.Center), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.ic_add), + contentDescription = "Requests", + tint = MaterialTheme.colorScheme.primary + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = "Requests", + color = MaterialTheme.colorScheme.primary, + fontWeight = FontWeight.Normal, + fontSize = 20.sp, + letterSpacing = 1.5.sp + ) + } + } + +} + +@Composable +fun Badges(badgeUris: List, modifier: Modifier = Modifier) { + LazyRow(modifier = modifier) { + items(badgeUris) { uri -> + AsyncImage( + model = uri, + contentDescription = "User Badge", + modifier = modifier + .clip(CircleShape) + .padding(16.dp) + .size(48.dp), + contentScale = ContentScale.Crop, + alignment = Alignment.Center + ) + } + } +} + +@Composable +private fun ProfilePicture(imageUri: String, modifier: Modifier = Modifier) { + AsyncImage( + model = imageUri, + contentDescription = "User profile picture", + modifier = modifier + .clip(CircleShape) + .size(80.dp), + contentScale = ContentScale.Crop, + alignment = Alignment.Center + ) +} + +@Composable +private fun UserDescription(description: String, modifier: Modifier = Modifier) { + Text( + text = description, modifier = modifier, + maxLines = 5, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.scrim, + fontFamily = MontserratFontFamily, + fontSize = 16.sp, + fontWeight = FontWeight.Normal + ) +} + +/** + * Displays the user name and user full name information. + */ +@Composable +private fun UserInfoColumn(username: String, userFullName: String, modifier: Modifier = Modifier) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.SpaceBetween, + horizontalAlignment = Alignment.CenterHorizontally + ) { + UserInfoText(username = username) + UserInfoText(username = userFullName) + } +} + +@Composable +private fun UserInfoText(username: String, modifier: Modifier = Modifier) { + Text( + text = username, + maxLines = 1, + modifier = modifier, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.scrim, + fontFamily = MontserratFontFamily, + fontSize = 22.sp, + fontWeight = FontWeight.Bold + ) +} + + +@Preview +@Composable +private fun ProfileEditButtonPreview() { + PredictionPollsTheme { + Column { + ProfileEditButton( + onProfileEditPressed = {}, + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .fillMaxWidth() + ) + Spacer(modifier = Modifier.height(16.dp)) + RequestsButton( + onRequestsClicked = {}, + modifier = Modifier + .border(1.dp, MaterialTheme.colorScheme.primary, RoundedCornerShape(12.dp)) + .clip(RoundedCornerShape(12.dp)) + .fillMaxWidth() + ) + } + } +} + + +@Preview +@Composable +private fun UsernameTextPrev() { + PredictionPollsTheme { + UserInfoColumn("can.gezer13", "Can Gezer") + } +} + + +@Preview +@Composable +private fun ProfileCardPreview() { + PredictionPollsTheme { + ProfileCard(modifier = Modifier.height(128.dp)) + } + +} + +@Preview(showBackground = true) +@Composable +private fun UserDescriptionPreview() { + PredictionPollsTheme { + UserDescription(description = "This is a long description text. Lorem ipsum dolor sit amet, consectet adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.") + } +} \ No newline at end of file diff --git a/prediction-polls/android/app/src/main/res/drawable/ic_add.xml b/prediction-polls/android/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 00000000..8f702491 --- /dev/null +++ b/prediction-polls/android/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,13 @@ + + + diff --git a/prediction-polls/android/app/src/main/res/drawable/ic_edit.xml b/prediction-polls/android/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 00000000..32c70eb9 --- /dev/null +++ b/prediction-polls/android/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,13 @@ + + + From 9f9b1972e2188bcf7dd6b88fd84b7ad4925932d3 Mon Sep 17 00:00:00 2001 From: Ahmet Emre Safak Date: Fri, 17 Nov 2023 22:25:49 +0300 Subject: [PATCH 002/246] create profile card --- .../predictionpolls/ui/profile/ProfileCard.kt | 160 ++++++++++++++---- 1 file changed, 124 insertions(+), 36 deletions(-) diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt index 87e3bd34..cbc7b797 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt @@ -8,8 +8,10 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -17,6 +19,7 @@ import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -25,11 +28,13 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage @@ -38,19 +43,109 @@ import com.bounswe.predictionpolls.ui.theme.MontserratFontFamily import com.bounswe.predictionpolls.ui.theme.PredictionPollsTheme @Composable -fun ProfileCard(modifier: Modifier = Modifier) { - Box( +fun ProfileCard( + username: String, + userFullName: String, + profilePictureUri: String, + userDescription: String, + badgeUris: List, + onProfileEditPressed: () -> Unit, + onRequestsClicked: () -> Unit, + modifier: Modifier = Modifier +) { + val paddingAroundContent: Dp = 16.dp + Column( modifier = modifier .clip(MaterialTheme.shapes.medium) - .background(MaterialTheme.colorScheme.primary) - .padding(16.dp) + .background(MaterialTheme.colorScheme.primaryContainer) .wrapContentHeight() .fillMaxWidth() ) { + CoverPhoto( + imageUri = profilePictureUri, + modifier = Modifier + .clip( + MaterialTheme.shapes.medium.copy( + bottomEnd = CornerSize(0.dp), + bottomStart = CornerSize(0.dp) + ) + ) + .aspectRatio(2.5f) + .fillMaxWidth() + ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(paddingAroundContent), + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.SpaceBetween + ) { + val profilePictureSize: Dp = 100.dp + Column( + modifier = Modifier.offset(y = profilePictureSize / -2f - (paddingAroundContent)), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally + ) { + ProfilePicture( + imageUri = profilePictureUri, + modifier = Modifier.size(profilePictureSize) + ) + UserInfoText(username = username) + UserInfoText(username = userFullName) + } + ProfileCardButtons( + onRequestsClicked = onRequestsClicked, + onProfileEditPressed = onProfileEditPressed, + modifier = Modifier + .weight(1f) + .padding(paddingAroundContent) + ) + + } + + UserDescription( + description = userDescription, + modifier = Modifier.padding(paddingAroundContent) + ) + + Badges(badgeUris = badgeUris, modifier = Modifier.padding(paddingAroundContent)) + } } +@Composable +private fun ProfileCardButtons( + onRequestsClicked: () -> Unit, + onProfileEditPressed: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + verticalArrangement = Arrangement.SpaceBetween, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier + ) { + ProfileEditButton( + onProfileEditPressed = onProfileEditPressed, + modifier = Modifier + .clip(MaterialTheme.shapes.medium) + .fillMaxWidth() + ) + Spacer(modifier = Modifier.height(16.dp)) + RequestsButton( + onRequestsClicked = onRequestsClicked, + modifier = Modifier + .border( + 1.dp, + MaterialTheme.colorScheme.primary, + MaterialTheme.shapes.medium + ) + .clip(MaterialTheme.shapes.medium) + .fillMaxWidth() + ) + } +} + @Composable private fun ProfileEditButton(onProfileEditPressed: () -> Unit, modifier: Modifier = Modifier) { Box( @@ -74,7 +169,7 @@ private fun ProfileEditButton(onProfileEditPressed: () -> Unit, modifier: Modifi text = "Edit Profile", color = MaterialTheme.colorScheme.onPrimary, fontWeight = FontWeight.Normal, - fontSize = 20.sp, + fontSize = 12.sp, letterSpacing = 1.5.sp ) } @@ -88,7 +183,8 @@ private fun CoverPhoto(imageUri: String, modifier: Modifier) { contentDescription = "User Badge", modifier = modifier, contentScale = ContentScale.Crop, - alignment = Alignment.Center + alignment = Alignment.Center, + error = painterResource(id = R.drawable.ic_warning), ) } @@ -115,7 +211,7 @@ private fun RequestsButton(onRequestsClicked: () -> Unit, modifier: Modifier = M text = "Requests", color = MaterialTheme.colorScheme.primary, fontWeight = FontWeight.Normal, - fontSize = 20.sp, + fontSize = 12.sp, letterSpacing = 1.5.sp ) } @@ -124,7 +220,7 @@ private fun RequestsButton(onRequestsClicked: () -> Unit, modifier: Modifier = M } @Composable -fun Badges(badgeUris: List, modifier: Modifier = Modifier) { +private fun Badges(badgeUris: List, modifier: Modifier = Modifier) { LazyRow(modifier = modifier) { items(badgeUris) { uri -> AsyncImage( @@ -132,8 +228,8 @@ fun Badges(badgeUris: List, modifier: Modifier = Modifier) { contentDescription = "User Badge", modifier = modifier .clip(CircleShape) - .padding(16.dp) - .size(48.dp), + .size(48.dp) + .background(Color.Red), contentScale = ContentScale.Crop, alignment = Alignment.Center ) @@ -167,21 +263,6 @@ private fun UserDescription(description: String, modifier: Modifier = Modifier) ) } -/** - * Displays the user name and user full name information. - */ -@Composable -private fun UserInfoColumn(username: String, userFullName: String, modifier: Modifier = Modifier) { - Column( - modifier = modifier, - verticalArrangement = Arrangement.SpaceBetween, - horizontalAlignment = Alignment.CenterHorizontally - ) { - UserInfoText(username = username) - UserInfoText(username = userFullName) - } -} - @Composable private fun UserInfoText(username: String, modifier: Modifier = Modifier) { Text( @@ -212,7 +293,7 @@ private fun ProfileEditButtonPreview() { RequestsButton( onRequestsClicked = {}, modifier = Modifier - .border(1.dp, MaterialTheme.colorScheme.primary, RoundedCornerShape(12.dp)) + .border(2.dp, MaterialTheme.colorScheme.primary, RoundedCornerShape(12.dp)) .clip(RoundedCornerShape(12.dp)) .fillMaxWidth() ) @@ -221,20 +302,27 @@ private fun ProfileEditButtonPreview() { } -@Preview -@Composable -private fun UsernameTextPrev() { - PredictionPollsTheme { - UserInfoColumn("can.gezer13", "Can Gezer") - } -} - - @Preview @Composable private fun ProfileCardPreview() { PredictionPollsTheme { - ProfileCard(modifier = Modifier.height(128.dp)) + ProfileCard( + "can.gezer13", + "Can Gezer", + "https://picsum.photos/400/400", + "This is a long description text. Lorem ipsum dolor sit amet, consectet adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.", + listOf( + "https://picsum.photos/400/400", + "https://picsum.photos/400/400", + "https://picsum.photos/400/400", + "https://picsum.photos/400/400", + "https://picsum.photos/400/400", + "https://picsum.photos/400/400", "https://picsum.photos/400/400" + ), + {}, + {}, + modifier = Modifier + ) } } From 1bf69ff7bd3bc74e71fb33770626303fc4f6683b Mon Sep 17 00:00:00 2001 From: Ahmet Emre Safak Date: Fri, 17 Nov 2023 22:42:18 +0300 Subject: [PATCH 003/246] create profile screen --- .../predictionpolls/ui/profile/ProfileCard.kt | 4 +- .../ui/profile/ProfileScreen.kt | 98 +++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt index cbc7b797..9da9cc33 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileCard.kt @@ -46,6 +46,7 @@ import com.bounswe.predictionpolls.ui.theme.PredictionPollsTheme fun ProfileCard( username: String, userFullName: String, + coverPhotoUri: String, profilePictureUri: String, userDescription: String, badgeUris: List, @@ -62,7 +63,7 @@ fun ProfileCard( .fillMaxWidth() ) { CoverPhoto( - imageUri = profilePictureUri, + imageUri = coverPhotoUri, modifier = Modifier .clip( MaterialTheme.shapes.medium.copy( @@ -310,6 +311,7 @@ private fun ProfileCardPreview() { "can.gezer13", "Can Gezer", "https://picsum.photos/400/400", + "https://picsum.photos/400/400", "This is a long description text. Lorem ipsum dolor sit amet, consectet adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo.", listOf( "https://picsum.photos/400/400", diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt new file mode 100644 index 00000000..4f291f2d --- /dev/null +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt @@ -0,0 +1,98 @@ +package com.bounswe.predictionpolls.ui.profile + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.bounswe.predictionpolls.ui.common.poll.DiscreteVoteOption +import com.bounswe.predictionpolls.ui.common.poll.PollComposable +import com.bounswe.predictionpolls.ui.theme.PredictionPollsTheme +import java.util.Date + +/** + * This composable is used to display profile information and polls of a user. + * @param profileInformation is a function that has access to lazy list scope of the profile screen that displays profile information of a user. + * @param polls is a function that has access to lazy list scope of the profile screen that displays polls of a user. + * Since these items have access to the lazy list scope, they can be added to the lazy list. + */ +@Composable +fun ProfileScreen( + profileInformation: @Composable () -> Unit, + polls: LazyListScope.() -> Unit, + modifier: Modifier = Modifier +) { + LazyColumn(modifier = modifier.background(MaterialTheme.colorScheme.surface)) { + + item { + profileInformation() + } + + this@LazyColumn.polls() + } +} + + +@Preview +@Composable +private fun ProfileScreenPreview() { + PredictionPollsTheme { + ProfileScreen( + profileInformation = { + ProfileCard( + username = "can.gezer13", + userFullName = "Can Gezer", + coverPhotoUri = "https://picsum.photos/400/400", + profilePictureUri = "https://picsum.photos/id/237/200/300", + userDescription = "I am a computer engineering student at Bogazici University. I am interested in machine learning and data science.", + badgeUris = listOf( + "https://picsum.photos/id/231/200/300", + "https://picsum.photos/id/232/200/300", + "https://picsum.photos/id/233/200/300" + ), + onProfileEditPressed = { }, + onRequestsClicked = { }, + modifier = Modifier.padding(16.dp) + ) + + }, + polls = { + items(10) { + PollComposable( + pollCreatorProfilePictureUri = "https://picsum.photos/id/236/200/300", + pollCreatorName = "Ahmet Yilmaz", + tags = listOf("Lebron", "James", "NBA"), + pollQuestionTitle = "Who is the best NBA player?", + optionsContent = { + Column { + DiscreteVoteOption( + optionName = "Lebron James", + voteCount = 150, + fillPercentage = 0.75f, + isSelected = false, + ) + DiscreteVoteOption( + optionName = "Michael Jordan", + voteCount = 50, + fillPercentage = 0.25f, + isSelected = false, + ) + } + + }, + modifier = Modifier.padding(16.dp), + dueDate = Date(), + rejectionText = "Last 5 days", + commentCount = 530 + ) + } + } + ) + } +} + From 4ed297c8efc1cd3ec0a26e26801a7827c950d308 Mon Sep 17 00:00:00 2001 From: EmreBatuhan <93476131+EmreBatuhan@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:53:07 +0300 Subject: [PATCH 004/246] Add profile router to app --- prediction-polls/backend/src/app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prediction-polls/backend/src/app.js b/prediction-polls/backend/src/app.js index 1e6053b2..f18f0e8d 100644 --- a/prediction-polls/backend/src/app.js +++ b/prediction-polls/backend/src/app.js @@ -1,6 +1,7 @@ const express = require('express'); const authRouter = require('./routes/AuthorizationRouter.js'); -const pollRouter = require('./routes/PollRouter.js'); +const pollRouter = require('./routes/PollRouter.js'); +const profileRouter = require('./routes/ProfileRouter.js'); const cors = require("cors"); @@ -19,6 +20,7 @@ app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x app.use('/polls',pollRouter); app.use('/auth', authRouter); +app.use('/profiles', profileRouter); const swaggerSpec = swaggerJsDoc(swaggerOptions); app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); From f5034db033fb05bf2ebe4b689b09f8b21422104b Mon Sep 17 00:00:00 2001 From: EmreBatuhan <93476131+EmreBatuhan@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:53:32 +0300 Subject: [PATCH 005/246] Update schema.sql --- prediction-polls/backend/src/schema.sql | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/prediction-polls/backend/src/schema.sql b/prediction-polls/backend/src/schema.sql index 2ed858e7..12d8f579 100644 --- a/prediction-polls/backend/src/schema.sql +++ b/prediction-polls/backend/src/schema.sql @@ -7,7 +7,8 @@ CREATE TABLE users ( email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, birthday DATETIME, - UNIQUE (username) + UNIQUE (username), + UNIQUE (email) ); CREATE TABLE refresh_tokens ( @@ -54,4 +55,15 @@ CREATE TABLE continuous_poll_selections ( selected_value FLOAT NOT NULL, FOREIGN KEY (poll_id) REFERENCES continuous_polls(id), FOREIGN KEY (user_id) REFERENCES users(id) +); + +CREATE TABLE profiles ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + username VARCHAR(255) NOT NULL, + profile_picture_data MEDIUMBLOB, + biography VARCHAR(5000), + birthday DATETIME, + is_hidden BOOLEAN DEFAULT False, + FOREIGN KEY (user_id) REFERENCES users(id) ); \ No newline at end of file From b98587f0dcaaddf6cbcee1c8c1ac861600b966d9 Mon Sep 17 00:00:00 2001 From: EmreBatuhan <93476131+EmreBatuhan@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:53:47 +0300 Subject: [PATCH 006/246] Create profile classes --- .../backend/src/repositories/ProfileDB.js | 93 +++++++++++++++++++ .../backend/src/routes/ProfileRouter.js | 33 +++++++ .../backend/src/services/ProfileService.js | 20 ++++ 3 files changed, 146 insertions(+) create mode 100644 prediction-polls/backend/src/repositories/ProfileDB.js create mode 100644 prediction-polls/backend/src/routes/ProfileRouter.js create mode 100644 prediction-polls/backend/src/services/ProfileService.js diff --git a/prediction-polls/backend/src/repositories/ProfileDB.js b/prediction-polls/backend/src/repositories/ProfileDB.js new file mode 100644 index 00000000..0bffa478 --- /dev/null +++ b/prediction-polls/backend/src/repositories/ProfileDB.js @@ -0,0 +1,93 @@ +const mysql = require('mysql2') + +require('dotenv').config(); + +const pool = mysql.createPool({ + host: process.env.MYSQL_HOST, + user: process.env.MYSQL_USER, + password: process.env.MYSQL_PASSWORD, + database: process.env.MYSQL_DATABASE +}).promise() + + +async function getProfileWithProfileId(profileId){ + const sql = 'SELECT * FROM profiles WHERE id= ?'; + + try { + const [rows, fields] = await pool.query(sql, [profileId]); + return rows; + } catch (error) { + console.error('getProfileWithProfileId(): Database Error'); + throw error; + } +} + +async function getProfileWithUserId(userId){ + const sql = 'SELECT * FROM profiles WHERE user_id= ?'; + + try { + const [rows, fields] = await pool.query(sql, [userId]); + return rows; + } catch (error) { + console.error('getProfileWithUserId(): Database Error'); + throw error; + } +} + +async function getProfileWithUserInfo({username,email}){ + const userId=0; + if(username){ + const sql = 'SELECT * FROM users WHERE username = ?'; + //TODO retrieve user id using username + userId = 1; + } + else{ + const sql = 'SELECT * FROM users WHERE email = ?'; + //TODO retrieve user id using email + userId = 1; + } + const user = await getProfileWithUserId(userId); + return user; +} + +async function addProfile({ + user_id, + username , + profile_picture, + biography, + birthday , + is_hidden}){ + + const sql = 'INSERT INTO profiles (user_id, username, profile_picture_data, biography, birthday, is_hidden) VALUES (?, ?, ?, ?, ?, ?)'; + values = [user_id,username,profile_picture,biography,birthday,is_hidden] + + const [resultSetHeader] = await pool.query(sql_poll, values); + profile_id = resultSetHeader.insertId; + if (!profile_id) { + return false; + } + return profile_id; +} + +async function updateProfile({ + user_id, + username , + profile_picture, + biography, + birthday , + is_hidden}){ + + if(profile_picture){ + + } + if(biography){ + + } + if(birthday){ + + } + if(is_hidden){ + + } +} + \ No newline at end of file diff --git a/prediction-polls/backend/src/routes/ProfileRouter.js b/prediction-polls/backend/src/routes/ProfileRouter.js new file mode 100644 index 00000000..4fd9e80f --- /dev/null +++ b/prediction-polls/backend/src/routes/ProfileRouter.js @@ -0,0 +1,33 @@ +const authenticator = require("../services/AuthorizationService.js"); +const service = require("../services/ProfileService.js"); +const express = require('express'); +const router = express.Router(); + +/** + * @swagger + * /polls/discrete: + * get: + * tags: + * - polls + * description: Get profile using profile id + */ +router.get('/:profileId', service.getProfileWithProfileId); + +/** + * @swagger + * /polls/discrete: + * get: + * tags: + * - polls + * description: Get profile using user data + */ + +router.get('/', service.getProfile); + +router.post('/', service.addProfile); + +router.patch('/', service.updateProfile); + + + +module.exports = router; \ No newline at end of file diff --git a/prediction-polls/backend/src/services/ProfileService.js b/prediction-polls/backend/src/services/ProfileService.js new file mode 100644 index 00000000..7089bedc --- /dev/null +++ b/prediction-polls/backend/src/services/ProfileService.js @@ -0,0 +1,20 @@ +const db = require("../repositories/PollDB.js"); + + +function getProfile(req,res){ + +} + +function getProfileWithProfileId(req,res){ + +} + +function addProfile(req,res){ + +} + +function updateProfile(req,res){ + +} + +module.exports = {getProfile,getProfileWithProfileId,addProfile,updateProfile} \ No newline at end of file From 53c71dd985649e30e516e8453d2f4f39a2d44991 Mon Sep 17 00:00:00 2001 From: Enes Furkan Arslan Date: Sat, 18 Nov 2023 19:30:04 +0300 Subject: [PATCH 007/246] Styling of create poll I have changed the view of buttons and their hover apperance. Also buttons that can be selected are now reflecting the mock up. Some texts are also changed to be same with mock up. --- .../src/Pages/Create/Create.module.css | 29 +++++- .../frontend/src/Pages/Create/index.jsx | 94 +++++++++++-------- 2 files changed, 81 insertions(+), 42 deletions(-) diff --git a/prediction-polls/frontend/src/Pages/Create/Create.module.css b/prediction-polls/frontend/src/Pages/Create/Create.module.css index 6634eefb..08524e99 100644 --- a/prediction-polls/frontend/src/Pages/Create/Create.module.css +++ b/prediction-polls/frontend/src/Pages/Create/Create.module.css @@ -20,17 +20,44 @@ } .questionContainer { + margin-top: 64px; margin-bottom: 16px; } .pollTypeContainer { margin-bottom: 16px; } + + .optButton { + padding-left: 25px; + padding-right: 25px; + border:none; + color: #fff; + } + + .optButton:hover { + border: none; + color:#fff !important ; + } .submitContainer { position: absolute; bottom: 40px; width: 40%; + + } + + .submitButton { + background-color: var(--secondary-500); + color: #fff; + padding-left: 25px; + padding-right: 25px; + border:none; + } + + .submitButton:hover { + background-color: var(--secondary-300); + color:#fff !important ; } .multipleChoiceInputs { @@ -39,6 +66,7 @@ .choiceInput { margin-bottom: 8px; + margin-right: 4px; } .datePickerContainer { @@ -70,4 +98,3 @@ position: absolute; bottom: 100px; } - diff --git a/prediction-polls/frontend/src/Pages/Create/index.jsx b/prediction-polls/frontend/src/Pages/Create/index.jsx index 00eb37b6..471fdcac 100644 --- a/prediction-polls/frontend/src/Pages/Create/index.jsx +++ b/prediction-polls/frontend/src/Pages/Create/index.jsx @@ -13,7 +13,7 @@ function Create() { const [additionalChoices, setAdditionalChoices] = useState(['']); const [customizedType, setCustomizedType] = useState('text'); const [customizedOptions, setCustomizedOptions] = useState(['']); - const [selectedDate, setSelectedDate] = useState(null); + const [selectedDate, setSelectedDate] = useState(null); const [setDueDate, setSetDueDate] = useState(false); const [dueDatePoll, setDueDatePoll] = useState(null); const [numericFieldValue, setNumericFieldValue] = useState(''); @@ -76,15 +76,17 @@ function Create() { // Additional logic will be added }; + + return (
Create
-

Create Poll

- + +