diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..de8896e
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..6ed36dd
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/.idea/deploymentTargetDropDown.xml b/frontend/.idea/deploymentTargetDropDown.xml
index 5d88396..495f769 100644
--- a/frontend/.idea/deploymentTargetDropDown.xml
+++ b/frontend/.idea/deploymentTargetDropDown.xml
@@ -7,11 +7,11 @@
-
+
-
+
\ No newline at end of file
diff --git a/frontend/app/build.gradle.kts b/frontend/app/build.gradle.kts
index 0c676a4..f129cb1 100644
--- a/frontend/app/build.gradle.kts
+++ b/frontend/app/build.gradle.kts
@@ -58,10 +58,10 @@ dependencies {
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
- implementation("androidx.compose.material3:material3")
implementation("com.google.android.material:material:1.11.0-beta01")
implementation("androidx.navigation:navigation-compose:2.7.4")
+ implementation("androidx.compose.material3:material3-android:1.2.0-alpha09")
implementation("androidx.navigation:navigation-runtime-ktx:2.7.4")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
@@ -79,6 +79,14 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.github.GrenderG:Toasty:1.5.2")
+ // The view calendar library
+ implementation("com.kizitonwose.calendar:view:2.4.0")
+ // The compose calendar library
+ implementation("com.kizitonwose.calendar:compose:2.4.0")
+ implementation("com.google.accompanist:accompanist-pager:0.12.0")
+ implementation("androidx.tv:tv-material:1.0.0-alpha10")
+ implementation("androidx.compose.runtime:runtime-livedata:1.5.4")
+
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1")
diff --git a/frontend/app/src/main/java/com/example/haengsha/MainActivity.kt b/frontend/app/src/main/java/com/example/haengsha/MainActivity.kt
index c2e2f71..c945665 100644
--- a/frontend/app/src/main/java/com/example/haengsha/MainActivity.kt
+++ b/frontend/app/src/main/java/com/example/haengsha/MainActivity.kt
@@ -1,8 +1,10 @@
package com.example.haengsha
+import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -11,6 +13,7 @@ import com.example.haengsha.ui.HaengshaApp
import com.example.haengsha.ui.theme.HaengshaTheme
class MainActivity : ComponentActivity() {
+ @RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
diff --git a/frontend/app/src/main/java/com/example/haengsha/model/dataSource/AppContainer.kt b/frontend/app/src/main/java/com/example/haengsha/model/dataSource/AppContainer.kt
index 8360750..aeb2c44 100644
--- a/frontend/app/src/main/java/com/example/haengsha/model/dataSource/AppContainer.kt
+++ b/frontend/app/src/main/java/com/example/haengsha/model/dataSource/AppContainer.kt
@@ -1,22 +1,36 @@
package com.example.haengsha.model.dataSource
+import com.example.haengsha.model.network.apiService.EventApiService
import com.example.haengsha.model.network.apiService.LoginApiService
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.OkHttpClient
import retrofit2.Retrofit
+import java.util.concurrent.TimeUnit
interface AppContainer {
val loginDataRepository: LoginDataRepository
+ val eventDataRepository: EventDataRepository
}
class HaengshaAppContainer : AppContainer {
- private val baseUrl = "http://ec2-43-201-28-141.ap-northeast-2.compute.amazonaws.com:8080/"
+ private val baseUrl = "http://ec2-52-79-228-36.ap-northeast-2.compute.amazonaws.com:8080/"
+ // Create an OkHttpClient with a default timeout
+ private val okHttpClient = OkHttpClient.Builder()
+ .readTimeout(30, TimeUnit.SECONDS) // Set the read timeout to 30 seconds
+ .connectTimeout(30, TimeUnit.SECONDS) // Set the connect timeout to 30 seconds
+ .build()
+
private val retrofit = Retrofit.Builder()
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.baseUrl(baseUrl)
+ .client(okHttpClient)
.build()
+ private val retrofitEventService: EventApiService by lazy { retrofit.create(EventApiService::class.java)}
+ override val eventDataRepository: EventDataRepository by lazy{ NetworkEventDataRepository(retrofitEventService)}
+
private val retrofitLoginService: LoginApiService by lazy {
retrofit.create(LoginApiService::class.java)
}
diff --git a/frontend/app/src/main/java/com/example/haengsha/model/dataSource/EventDataRepository.kt b/frontend/app/src/main/java/com/example/haengsha/model/dataSource/EventDataRepository.kt
new file mode 100644
index 0000000..7d08c41
--- /dev/null
+++ b/frontend/app/src/main/java/com/example/haengsha/model/dataSource/EventDataRepository.kt
@@ -0,0 +1,17 @@
+package com.example.haengsha.model.dataSource
+
+import com.example.haengsha.model.network.apiService.EventApiService
+import com.example.haengsha.model.network.apiService.EventResponse
+
+interface EventDataRepository {
+ suspend fun getEventByDate(eventType: Int, date: String): List
+}
+
+
+class NetworkEventDataRepository(
+ private val eventApiService: EventApiService
+) : EventDataRepository {
+ override suspend fun getEventByDate(eventType: Int, date: String): List {
+ return eventApiService.getEventByDate(eventType, date)
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/src/main/java/com/example/haengsha/model/network/apiService/EventApiService.kt b/frontend/app/src/main/java/com/example/haengsha/model/network/apiService/EventApiService.kt
new file mode 100644
index 0000000..717aee5
--- /dev/null
+++ b/frontend/app/src/main/java/com/example/haengsha/model/network/apiService/EventApiService.kt
@@ -0,0 +1,76 @@
+package com.example.haengsha.model.network.apiService
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import com.example.haengsha.ui.screens.home.EventCardData
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import retrofit2.http.GET
+import retrofit2.http.Path
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+
+@Serializable
+data class EventResponse(
+ @SerialName("nickname") val organizer: String,
+ @SerialName("title") val title: String,
+ @SerialName("content") val content: String,
+ @SerialName("place") val place: String,
+ @SerialName("image") val image: String?,
+ @SerialName("is_festival") val isFestival: Boolean,
+ @SerialName("like_count") val likeCount: Int,
+ @SerialName("favorite_count") val favoriteCount: Int,
+ @SerialName("time") val time: String,
+ @SerialName("event_durations") val eventDurations: List
+)
+
+@Serializable
+data class EventDurationResponse(
+ @SerialName("event_day") val eventDay: String
+)
+
+@RequiresApi(Build.VERSION_CODES.O)
+fun EventResponse.toEventCardData(): EventCardData {
+ var startDate = stringToDate(eventDurations[0].eventDay)
+ var endDate = startDate
+ if (eventDurations.size>1) {
+ endDate = stringToDate(eventDurations[eventDurations.size-1].eventDay)
+ }
+
+ var eventType = "Festival"
+ if (!isFestival){
+ eventType = "Academic"
+ }
+ return EventCardData(
+ organizer = organizer,
+ eventTitle = title,
+ startDate = startDate,
+ endDate = endDate,
+ likes = likeCount,
+ favorites = favoriteCount,
+ eventType = eventType,
+ place = place,
+ time = time
+ )
+}
+
+interface EventApiService {
+ @GET("/api/post/festival/{eventType}/date/{date}/{date}")
+ suspend fun getEventByDate(
+ @Path("eventType") eventType: Int, // Replace with appropriate type
+ @Path("date") date: String
+ ): List // Change the return type to a list of EventResponse
+}
+
+@RequiresApi(Build.VERSION_CODES.O)
+fun stringToDate(dateString: String): LocalDate {
+ var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
+ try {
+ return LocalDate.parse(dateString, formatter)
+ //val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+ //date = format.parse(dateString)!!
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return LocalDate.now()
+}
diff --git a/frontend/app/src/main/java/com/example/haengsha/model/viewModel/event/EventViewModel.kt b/frontend/app/src/main/java/com/example/haengsha/model/viewModel/event/EventViewModel.kt
new file mode 100644
index 0000000..61b05a6
--- /dev/null
+++ b/frontend/app/src/main/java/com/example/haengsha/model/viewModel/event/EventViewModel.kt
@@ -0,0 +1,75 @@
+package com.example.haengsha.model.viewModel.event
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.initializer
+import androidx.lifecycle.viewmodel.viewModelFactory
+import com.example.haengsha.HaengshaApplication
+import com.example.haengsha.model.dataSource.EventDataRepository
+import com.example.haengsha.model.network.apiService.EventResponse
+import com.example.haengsha.model.network.apiService.toEventCardData
+import com.example.haengsha.ui.screens.home.EventCardData
+import com.example.haengsha.ui.screens.home.SharedViewModel
+import kotlinx.coroutines.launch
+import retrofit2.HttpException
+import java.io.Closeable
+import java.io.IOException
+import java.time.LocalDate
+
+@RequiresApi(Build.VERSION_CODES.O)
+open class EventViewModel(
+ private val eventDataRepository: EventDataRepository,
+ private val sharedViewModel: SharedViewModel
+) : ViewModel() {
+
+ companion object {
+ private lateinit var sharedViewModelInstance: SharedViewModel
+
+ fun Factory(sharedViewModel: SharedViewModel): ViewModelProvider.Factory {
+ sharedViewModelInstance = sharedViewModel
+ return viewModelFactory {
+ initializer {
+ val application =
+ this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as HaengshaApplication
+ val eventDataRepository = application.container.eventDataRepository
+ EventViewModel(eventDataRepository, sharedViewModel)
+ }
+ }
+ }
+ }
+
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun getEventByDate(eventType: String, date: LocalDate) {
+ viewModelScope.launch {
+ try {
+ var eventTypeConverted = if (eventType == "Festival") 1 else 0
+
+ val eventGetResponse: List =
+ eventDataRepository.getEventByDate(eventTypeConverted, date.toString())
+
+ val eventCardDataList: List =
+ eventGetResponse.map { it.toEventCardData() }
+
+ if (eventType == "Festival") {
+ sharedViewModel.updateFestivalItems(eventCardDataList)
+ } else {
+ sharedViewModel.updateAcademicItems(eventCardDataList)
+ }
+
+ sharedViewModel.updateSelectedDate(date)
+
+ } catch (e: HttpException) {
+ val errorMessage = e.response()?.errorBody()?.string() ?: "이벤트를 불러오지 못했습니다."
+ } catch (e: IOException) {
+ val errorMessage = "이벤트를 불러오지 못했습니다."
+ } catch (e: Exception) {
+ val errorMessage = "이벤트를 불러오지 못했습니다."
+ e.printStackTrace()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/src/main/java/com/example/haengsha/ui/HaengshaApp.kt b/frontend/app/src/main/java/com/example/haengsha/ui/HaengshaApp.kt
index 0feaf66..f987bd8 100644
--- a/frontend/app/src/main/java/com/example/haengsha/ui/HaengshaApp.kt
+++ b/frontend/app/src/main/java/com/example/haengsha/ui/HaengshaApp.kt
@@ -1,5 +1,7 @@
package com.example.haengsha.ui
+import android.os.Build
+import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -17,6 +19,7 @@ import com.example.haengsha.ui.screens.home.Home
import com.example.haengsha.ui.screens.login.Login
import com.example.haengsha.ui.screens.setting.Setting
+@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun HaengshaApp() {
val userViewModel: UserViewModel = viewModel()
@@ -36,7 +39,6 @@ fun HaengshaApp() {
}
composable(MainRoute.Home.route) {
Home(
- userUiState = userUiState,
mainNavController = mainNavController
)
}
diff --git a/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/EventCard.kt b/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/EventCard.kt
new file mode 100644
index 0000000..5e9b7f8
--- /dev/null
+++ b/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/EventCard.kt
@@ -0,0 +1,115 @@
+package com.example.haengsha.ui.screens.home
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.BorderStroke
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedCard
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.example.haengsha.ui.theme.LikePink
+import com.example.haengsha.ui.theme.md_theme_light_onSurfaceVariant
+import com.example.haengsha.ui.theme.poppins
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+
+@RequiresApi(Build.VERSION_CODES.O)
+fun formatDateToMMDD(date: LocalDate): String {
+ val formatter = DateTimeFormatter.ofPattern("MM-dd")
+ return date.format(formatter)
+}
+
+
+@RequiresApi(Build.VERSION_CODES.O)
+@Composable
+fun EventCard(
+ organizer: String,
+ eventTitle: String,
+ startDate: LocalDate,
+ endDate: LocalDate,
+ likes: Int,
+) {
+ val formattedStartDate = formatDateToMMDD(startDate)
+ val formattedEndDate = formatDateToMMDD(endDate)
+
+ OutlinedCard(
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surface,
+ ),
+ border = BorderStroke(1.dp, md_theme_light_onSurfaceVariant),
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(150.dp)
+ .padding(all = 16.dp),
+
+ ) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(2f)
+ ) {
+ Text(
+ text = organizer + "\n" + eventTitle, // 기관 명 + 행사 명 -> Text로 변경
+ modifier = Modifier
+ .padding(16.dp)
+ .align(Alignment.CenterStart),
+ textAlign = TextAlign.Left,
+ fontSize = 16.sp,
+ fontFamily = poppins
+ )
+ }
+
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f)
+ ) {
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.End
+ ) {
+ Text(
+ text = "$formattedStartDate - $formattedEndDate",
+ modifier = Modifier
+ .padding(16.dp)
+ .align(Alignment.End),
+ textAlign = TextAlign.Right,
+ fontFamily = poppins,
+ )
+ Text(
+ text = "♥ $likes", // 좋아요 수 -> Text로 변경
+ modifier = Modifier
+ .padding(16.dp)
+ .align(Alignment.End),
+ textAlign = TextAlign.Right,
+ color = LikePink,
+ fontFamily = poppins
+ )
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/Home.kt b/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/Home.kt
index b68752d..0ca4b12 100644
--- a/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/Home.kt
+++ b/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/Home.kt
@@ -1,30 +1,223 @@
package com.example.haengsha.ui.screens.home
+import android.annotation.SuppressLint
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.background
+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.Spacer
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material3.Button
+import androidx.compose.material3.DatePicker
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
-import com.example.haengsha.model.uiState.UserUiState
+import androidx.navigation.compose.rememberNavController
+import com.example.haengsha.model.viewModel.event.EventViewModel
+import com.example.haengsha.ui.theme.HaengshaBlue
+import com.kizitonwose.calendar.compose.WeekCalendar
+import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState
+import com.kizitonwose.calendar.core.atStartOfMonth
+import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale
+import java.time.DayOfWeek
+import java.time.LocalDate
+import java.time.YearMonth
+import java.time.format.DateTimeFormatter
+import java.time.format.TextStyle
+import java.util.Locale
+
+class SharedViewModel() : ViewModel() {
+ @RequiresApi(Build.VERSION_CODES.O)
+ private val _selectedDate = MutableLiveData(LocalDate.now())
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ val selectedDate: LiveData = _selectedDate
+
+ // Initialize _festivalItems and _academicItems with initial data
+ @RequiresApi(Build.VERSION_CODES.O)
+ private val _festivalItems = MutableLiveData>()
+ @RequiresApi(Build.VERSION_CODES.O)
+ private val _academicItems = MutableLiveData>()
+
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ val festivalItems: LiveData> = _festivalItems
+
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ val academicItems: LiveData> = _academicItems
+
+
+ // Update functions to set LiveData properties
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun updateSelectedDate(newDate: LocalDate) {
+ _selectedDate.value = newDate
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun updateFestivalItems(newItems: List) {
+ _festivalItems.value = newItems
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun updateAcademicItems(newItems: List) {
+ _academicItems.value = newItems
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.O)
+fun formatDateToYYYYMMDD(date: MutableLiveData): String {
+ val localDate = date.value ?: LocalDate.now()
+ val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
+ return localDate.format(formatter)
+}
+
+
+@OptIn(ExperimentalMaterial3Api::class)
+@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
+@RequiresApi(Build.VERSION_CODES.O)
@Composable
-fun Home(userUiState: UserUiState, mainNavController: NavController) {
- HomeScreen(userUiState = userUiState)
+fun HomeScreen(sharedViewModel: SharedViewModel) {
+ val eventViewModel: EventViewModel =
+ viewModel(factory = EventViewModel.Factory(sharedViewModel))
+ val currentDate = remember { LocalDate.now() }
+ val currentMonth = remember { YearMonth.now() }
+ val startDate = remember { currentMonth.minusMonths(100).atStartOfMonth() } // Adjust as needed
+ val endDate = remember { currentMonth.plusMonths(100).atEndOfMonth() } // Adjust as needed
+ val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library
+ var selection by remember { mutableStateOf(currentDate) }
+ val state = rememberWeekCalendarState(
+ startDate = startDate,
+ endDate = endDate,
+ firstVisibleWeekDate = currentDate,
+ firstDayOfWeek = firstDayOfWeek
+ )
+
+ var isDatePickerDialogVisible by remember { mutableStateOf(false )}
+
+ Scaffold(topBar = {
+ TopAppBar(
+ title = { Text(text = "Home") },
+ )
+ }) { padding ->
+ Column (
+ modifier = Modifier.fillMaxSize().padding(padding)
+ ) {
+ WeekCalendar(
+ state = state,
+ dayContent = { day ->
+ Day(day.date, isSelected = selection == day.date) { clicked ->
+ if (selection != clicked) {
+ selection = clicked
+ //pickDate = clicked
+ eventViewModel.getEventByDate(eventType = "Academic", clicked)
+ eventViewModel.getEventByDate(eventType = "Festival", clicked)
+ }
+ }
+ },
+ )
+ TabView(sharedViewModel, selection, 0)
+ Button(
+ onClick = {
+ isDatePickerDialogVisible = true
+ }
+ ) {
+ Text("Pick Date")
+ }
+ }
+
+
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.O)
+private val dateFormatter = DateTimeFormatter.ofPattern("dd")
+
+@RequiresApi(Build.VERSION_CODES.O)
+fun DayOfWeek.displayText(uppercase: Boolean = false): String {
+ return getDisplayName(TextStyle.SHORT, Locale.ENGLISH).let { value ->
+ if (uppercase) value.uppercase(Locale.ENGLISH) else value
+ }
}
+@RequiresApi(Build.VERSION_CODES.O)
@Composable
-fun HomeScreen(userUiState: UserUiState) {
- /* TODO 여기에 홈 UI 넣기 */
+private fun Day(date: LocalDate, isSelected: Boolean, onClick: (LocalDate) -> Unit) {
Box(
- Modifier.fillMaxSize(),
- contentAlignment = Alignment.Center
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentHeight()
+ .clickable { onClick(date) },
+ contentAlignment = Alignment.Center,
) {
- Column {
- Text(text = "This is HomeScreen")
- Text(text = "User token: ${userUiState.token}")
+ Column(
+ modifier = Modifier.padding(vertical = 10.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(6.dp),
+ ) {
+ Text(
+ text = date.dayOfWeek.displayText(),
+ fontSize = 12.sp,
+ fontWeight = FontWeight.Light,
+ )
+ Text(
+ text = dateFormatter.format(date),
+ fontSize = 14.sp,
+ fontWeight = FontWeight.Bold,
+ )
+ }
+ if (isSelected) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(5.dp)
+ .background(HaengshaBlue)
+ .align(Alignment.BottomCenter),
+ )
}
}
+}
+
+@RequiresApi(Build.VERSION_CODES.O)
+@Composable
+fun Home(mainNavController: NavController) {
+ val sharedViewModel = viewModel()
+ Column {
+ HomeScreen(sharedViewModel)
+
+ }
+}
+
+@RequiresApi(Build.VERSION_CODES.O)
+@Preview(showBackground = true)
+@Composable
+fun HomeScreenPreview() {
+ Home(rememberNavController())
}
\ No newline at end of file
diff --git a/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/TabView.kt b/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/TabView.kt
new file mode 100644
index 0000000..ae8e6c8
--- /dev/null
+++ b/frontend/app/src/main/java/com/example/haengsha/ui/screens/home/TabView.kt
@@ -0,0 +1,423 @@
+package com.example.haengsha.ui.screens.home
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.PrimaryTabRow
+import androidx.compose.material3.Tab
+import androidx.compose.material3.TabRowDefaults
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.shadow
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import com.example.haengsha.R
+import com.example.haengsha.model.viewModel.event.EventViewModel
+import com.example.haengsha.ui.theme.HaengshaBlue
+import com.example.haengsha.ui.theme.LikePink
+import com.example.haengsha.ui.theme.md_theme_light_onSurfaceVariant
+import com.example.haengsha.ui.theme.poppins
+import kotlinx.coroutines.launch
+import java.time.LocalDate
+
+data class TabItem(
+ val title: String, val eventCards: List
+)
+
+
+data class EventCardData(
+ val organizer: String,
+ val eventTitle: String,
+ val startDate: LocalDate,
+ val endDate: LocalDate,
+ val likes: Int,
+ val favorites: Int,
+ val eventType: String,
+ val place: String = "",
+ val time: String = ""
+ // val Image: // Image URL 변경 필요 (임시로 nudge_image 사용함)
+)
+
+
+@RequiresApi(Build.VERSION_CODES.O)
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun TabView(sharedViewModel: SharedViewModel, selectedDate: LocalDate, selectedTabIndex: Int) {
+
+ val academicItems by sharedViewModel.academicItems.observeAsState()
+ val festivalItems by sharedViewModel.festivalItems.observeAsState()
+ var showDialog by remember { mutableStateOf(false) }
+ var selectedEvent: EventCardData? by remember { mutableStateOf(null) }
+ var showEventCardPopup by remember { mutableStateOf(false) }
+ val tabItems = listOf(academicItems?.let {
+ TabItem(
+ title = "Academic", eventCards = it
+ )
+ }, festivalItems?.let {
+ TabItem(
+ title = "Festival", eventCards = it
+ )
+ })
+
+ // Remember the selected tab
+ var selectedTabIndex by remember { mutableIntStateOf(0) }
+
+ // Pager state
+ var pagerState = rememberPagerState {
+ tabItems.size
+ }
+
+ // Coroutine scope
+ val coroutineScope = rememberCoroutineScope()
+
+ Column(modifier = Modifier.fillMaxSize()) {
+ // Tab row
+ PrimaryTabRow(selectedTabIndex = selectedTabIndex,
+ contentColor = md_theme_light_onSurfaceVariant,
+ indicator = { tabPositions ->
+ TabRowDefaults.PrimaryIndicator(
+ modifier = Modifier.tabIndicatorOffset(tabPositions[0]), color = HaengshaBlue
+ )
+ }) {
+ // Tab items
+ tabItems.forEachIndexed { index, item ->
+ Tab(
+ selected = (index == selectedTabIndex),
+ onClick = {
+ selectedTabIndex = index
+ // Change the page when the tab is changed
+ coroutineScope.launch {
+ pagerState.animateScrollToPage(selectedTabIndex)
+ }
+ },
+ text = {
+ if (index == 0) Text(text = "Academic")
+ else Text(text = "Festival")
+ },
+ )
+ }
+ }
+
+ // Pager
+ HorizontalPager(
+ state = pagerState,
+ modifier = Modifier.fillMaxWidth(),
+
+ ) { index ->
+
+ // App content
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ item {
+ // Button at the top of the HorizontalPager
+ Button(
+ onClick = {
+ showDialog = true
+ },
+ colors = androidx.compose.material3.ButtonDefaults.buttonColors(HaengshaBlue),
+ modifier = Modifier
+ .padding(16.dp)
+ .shadow(
+ elevation = 10.dp,
+ spotColor = Color(0x1A18274B),
+ ambientColor = Color(0x1A18274B)
+ )
+ .shadow(
+ elevation = 10.dp,
+ spotColor = Color(0x26000000),
+ ambientColor = Color(0x26000000)
+ )
+ .width(200.dp)
+ .height(50.dp)
+
+ ) {
+ Text(text = "맞춤 추천 받기")
+ }
+ }
+ val itemsToDisplay = if (index == 1) festivalItems else academicItems
+ //val itemsToDisplay = if (index == 1) festivalItems else academicItems
+ items(itemsToDisplay.orEmpty()) { eventCardData ->
+
+ Box(modifier = Modifier.clickable {
+ showEventCardPopup = true
+ selectedEvent = eventCardData
+ }) {
+ EventCard(
+ organizer = eventCardData.organizer,
+ eventTitle = eventCardData.eventTitle,
+ startDate = eventCardData.startDate,
+ endDate = eventCardData.endDate,
+ likes = eventCardData.likes
+ )
+ }
+ }
+ }
+ }
+
+ if (showDialog) {
+ // Display the AlertDialog with "Here is popup"
+ AlertDialog(onDismissRequest = {
+ // Close the dialog when clicked outside
+ showDialog = false
+ }, title = {
+ Text(text = "Popup Title")
+ }, text = {
+ Text(text = "Here is popup")
+ }, confirmButton = {
+ Button(onClick = {
+ // Handle confirm button click
+ showDialog = false
+ }) {
+ Text("OK")
+ }
+ }, dismissButton = {
+ Button(onClick = {
+ // Handle dismiss button click
+ showDialog = false
+ }) {
+ Text("Cancel")
+ }
+ })
+ }
+
+ var buttonWidth by remember { mutableStateOf(0.dp) }
+ var buttonHeight by remember { mutableStateOf(0.dp) }
+
+ if (showEventCardPopup) {
+ AlertDialog(
+ onDismissRequest = {
+ // Close the popup when clicked outside
+ showEventCardPopup = false
+ },
+ title = {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(
+ 8.dp, Alignment.CenterVertically
+ ),
+ horizontalAlignment = Alignment.Start,
+ ) {
+
+ Text(
+ text = selectedEvent?.eventTitle ?: "N/A", style = TextStyle(
+ fontSize = 18.sp,
+ lineHeight = 20.sp,
+ fontFamily = FontFamily(Font(R.font.poppins_bold)),
+ fontWeight = FontWeight(400),
+ color = Color(0xFF343A40),
+ )
+ )
+
+ Row {
+
+ Text(
+ text = selectedEvent?.eventType ?: "N/A", style = TextStyle(
+ fontSize = 11.sp,
+ lineHeight = 17.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(400),
+ color = Color(0xFF868E96),
+ )
+ )
+ Text(
+ text = " | ", style = TextStyle(
+ fontSize = 11.sp,
+ lineHeight = 17.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(400),
+ color = Color(0xFF868E96),
+ )
+ )
+
+ Text(
+ text = selectedEvent?.organizer ?: "N/A", style = TextStyle(
+ fontSize = 11.sp,
+ lineHeight = 17.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(400),
+ color = Color(0xFF868E96),
+ )
+ )
+ }
+
+ }
+ },
+ text = {
+ // Use a Column to ensure proper spacing of text
+ Column {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp), // Adjust the padding as needed
+ contentAlignment = Alignment.Center
+
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.nudge_image),
+ contentDescription = "image description",
+ contentScale = ContentScale.Crop, // Maintain aspect ratio
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+
+ Column {
+
+ val startDateText =
+ selectedEvent?.startDate?.let { formatDateToMMDD(it) }
+ val endDateText = selectedEvent?.endDate?.let { formatDateToMMDD(it) }
+
+
+ Text(
+ text = "주최 | " + selectedEvent?.organizer, style = TextStyle(
+ fontSize = 12.sp,
+ lineHeight = 19.56.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(500),
+ color = Color(0xFF000000),
+ textAlign = TextAlign.Center,
+ )
+ )
+
+ Text(
+ text = "일자 | $startDateText - $endDateText", style = TextStyle(
+ fontSize = 12.sp,
+ lineHeight = 19.56.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(500),
+ color = Color(0xFF000000),
+ textAlign = TextAlign.Center,
+ )
+ )
+
+ Text(
+ text = "장소 | " + selectedEvent?.place, style = TextStyle(
+ fontSize = 12.sp,
+ lineHeight = 19.56.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(500),
+ color = Color(0xFF000000),
+ textAlign = TextAlign.Center,
+ )
+ )
+
+ Text(
+ text = "시간 | " + selectedEvent?.time, style = TextStyle(
+ fontSize = 12.sp,
+ lineHeight = 19.56.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(500),
+ color = Color(0xFF000000),
+ textAlign = TextAlign.Center,
+ )
+ )
+
+ Row(modifier = Modifier.padding(top = 10.dp)) {
+ Image(
+ painter = painterResource(id = R.drawable.like_fill_icon),
+ contentDescription = "image description",
+ )
+
+ Text(
+ text = selectedEvent?.likes.toString(), style = TextStyle(
+ fontSize = 10.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(500),
+ color = LikePink,
+ textAlign = TextAlign.Center,
+ )
+ )
+ }
+ }
+ }
+ },
+ confirmButton = {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(0.dp)
+ ) {
+ Button(
+ onClick = {
+ showEventCardPopup = false
+ },
+ modifier = Modifier
+ .wrapContentWidth()
+ .wrapContentHeight()
+ .padding(0.dp), // 패딩 제거
+
+ colors = androidx.compose.material3.ButtonDefaults.buttonColors(Color.White),
+
+ ) {
+ Text(
+ text = "닫기",
+ style = TextStyle(
+ fontSize = 13.sp,
+ fontFamily = poppins,
+ fontWeight = FontWeight(500),
+ color = Color(0xFF000000),
+ textAlign = TextAlign.Center,
+ textDecoration = TextDecoration.Underline,
+ ),
+ modifier = Modifier.padding(0.dp) // 텍스트 주위의 패딩 제거
+ )
+ }
+ }
+ },
+ modifier = Modifier
+ .shadow(
+ elevation = 10.dp,
+ spotColor = Color(0x40000000),
+ ambientColor = Color(0x40000000)
+ )
+ .width(300.dp)
+ .height(550.dp)
+ .background(color = Color(0xFFFFFFFF)),
+ containerColor = Color(0xFFFFFFFF)
+ )
+ }
+ }
+}
diff --git a/frontend/build.gradle.kts b/frontend/build.gradle.kts
index b4cf450..0403714 100644
--- a/frontend/build.gradle.kts
+++ b/frontend/build.gradle.kts
@@ -2,4 +2,4 @@
plugins {
id("com.android.application") version "8.1.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
-}
\ No newline at end of file
+}