Украл все, что можно, каким то чудом работает, сейчас буду норм переделывать под PDF
This commit is contained in:
parent
41d688791d
commit
61faa1af74
@ -5,6 +5,7 @@ import com.example.myapplication.api.rent.RentRemote
|
||||
import com.example.myapplication.api.item.ItemFromBikeRemote
|
||||
import com.example.myapplication.api.item.ItemRemote
|
||||
import com.example.myapplication.api.item.ItemWithBikeRemote
|
||||
import com.example.myapplication.api.report.ReportRemote
|
||||
import com.example.myapplication.api.user.UserRemote
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import kotlinx.serialization.json.Json
|
||||
@ -23,6 +24,12 @@ import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface MyServerService {
|
||||
|
||||
@GET("report")
|
||||
suspend fun getReportInfo(
|
||||
@Query("fromDate") fromDate: String,
|
||||
@Query("toDate") toDate: String
|
||||
): List<ReportRemote>
|
||||
@GET("rents")
|
||||
suspend fun getRents(): List<RentRemote>
|
||||
@GET("users/{id}")
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.example.myapplication.api.item
|
||||
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.report.ReportRemote
|
||||
import com.example.myapplication.database.entities.model.Item
|
||||
import com.example.myapplication.database.entities.repository.OfflineRentItemRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineItemRepository
|
||||
@ -48,4 +49,8 @@ class RestItemRepository(
|
||||
dbRentItemRepository.deleteItemsByUid(item.uid)
|
||||
dbItemRepository.deleteItem(item)
|
||||
}
|
||||
suspend fun getReport(fromDate: String, toDate: String):List<ReportRemote>
|
||||
{
|
||||
return service.getReportInfo(fromDate,toDate)
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.example.myapplication.api.report
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReportRemote(
|
||||
val id: Int = 0,
|
||||
val dateTime: Int = 0,
|
||||
val weight: Double = 0.0,
|
||||
val maxCount: Int = 0,
|
||||
val bikeId: Int = 0
|
||||
)
|
@ -55,6 +55,7 @@ import com.example.myapplication.database.entities.composeui.CurrentUserViewMode
|
||||
import com.example.myapplication.database.entities.composeui.LoginScreen
|
||||
import com.example.myapplication.database.entities.composeui.RentList
|
||||
import com.example.myapplication.database.entities.composeui.RentView
|
||||
import com.example.myapplication.database.entities.composeui.ReportPage
|
||||
import com.example.myapplication.database.entities.composeui.UserProfile
|
||||
import com.example.myapplication.database.entities.composeui.edit.BikeEdit
|
||||
import com.example.myapplication.database.entities.composeui.edit.ItemEdit
|
||||
@ -190,6 +191,7 @@ fun Navhost(
|
||||
composable(Screen.RentList.route) { RentList(navController, currentUserViewModel = currentUserViewModel) }
|
||||
composable(Screen.Cart.route) { Cart(currentUserViewModel = currentUserViewModel) }
|
||||
composable(Screen.UserProfile.route) { UserProfile(navController,isDarkTheme, dataStore, currentUserViewModel = currentUserViewModel) }
|
||||
composable(Screen.Report.route) { ReportPage(navController = navController) }
|
||||
composable(Screen.LoginScreen.route) { LoginScreen(navController) }
|
||||
composable(
|
||||
Screen.BikeEdit.route,
|
||||
|
@ -15,6 +15,9 @@ enum class Screen(
|
||||
val icon: ImageVector = Icons.Filled.Favorite,
|
||||
val showInBottomBar: Boolean = true
|
||||
) {
|
||||
Report(
|
||||
"report", R.string.report
|
||||
),
|
||||
BikeList(
|
||||
"Bike-list", R.string.Bike_main_title, Icons.Filled.Home
|
||||
),
|
||||
|
@ -23,6 +23,11 @@ object AppViewModelProvider {
|
||||
initializer {
|
||||
BikeListViewModel(bikeApplication().container.bikeRestRepository)
|
||||
}
|
||||
initializer {
|
||||
ReportViewModel(
|
||||
bikeApplication().container.itemRestRepository,
|
||||
)
|
||||
}
|
||||
initializer {
|
||||
BikeEditViewModel(
|
||||
this.createSavedStateHandle(),
|
||||
|
@ -0,0 +1,156 @@
|
||||
package com.example.myapplication.database.entities.composeui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
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.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DisplayMode
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.example.myapplication.R
|
||||
import com.example.myapplication.api.report.ReportRemote
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Date
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ReportPage (navController: NavController?, viewModel: ReportViewModel = viewModel(factory = AppViewModelProvider.Factory))
|
||||
{
|
||||
|
||||
val dateStateStart = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
|
||||
val dateStateEnd = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val reportResultPageState = viewModel.reportResultPageUiState
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
)
|
||||
{
|
||||
Text(
|
||||
text = stringResource(id = R.string.startDate),
|
||||
style = MaterialTheme.typography.headlineLarge
|
||||
)
|
||||
DatePicker(
|
||||
state = dateStateStart,
|
||||
)
|
||||
val selectedDateStart = dateStateStart.selectedDateMillis
|
||||
if (selectedDateStart != null) {
|
||||
val resultDate= Date(selectedDateStart)
|
||||
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(startDate = resultDate))
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(startDate = Date(0)))
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.endDate),
|
||||
style = MaterialTheme.typography.headlineLarge
|
||||
)
|
||||
|
||||
DatePicker(
|
||||
state = dateStateEnd,
|
||||
)
|
||||
val selectedDateEnd = dateStateEnd.selectedDateMillis
|
||||
if (selectedDateEnd != null) {
|
||||
val resultDate = Date(selectedDateEnd)
|
||||
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(endDate = resultDate))
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(endDate = Date(0)))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Button(
|
||||
onClick = {coroutineScope.launch { viewModel.getReport() } },
|
||||
enabled = viewModel.reportPageUiState.isEntryValid,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 10.dp)
|
||||
.border(4.dp, MaterialTheme.colorScheme.onPrimary, shape = RoundedCornerShape(10.dp)),
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary)
|
||||
) {
|
||||
Text("Сформировать отчет")
|
||||
}
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Text(
|
||||
text = "Результат",
|
||||
style = MaterialTheme.typography.headlineLarge
|
||||
)
|
||||
TableScreen(reportData = reportResultPageState.resReport)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RowScope.TableCell(
|
||||
text: String,
|
||||
weight: Float
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
Modifier
|
||||
.border(1.dp, Color.Black)
|
||||
.weight(weight)
|
||||
.padding(8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun TableScreen(reportData: List<ReportRemote>) {
|
||||
|
||||
val column1Weight = .3f // 30%
|
||||
val column2Weight = 1f // 30%
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.padding(16.dp)) {
|
||||
|
||||
Row(Modifier.background(Color.White)) {
|
||||
TableCell(text = "ID", weight = column1Weight)
|
||||
TableCell(text = "Товар", weight = column1Weight)
|
||||
TableCell(text = "Кол-во", weight = column1Weight)
|
||||
TableCell(text = "Цена", weight = column1Weight)
|
||||
}
|
||||
|
||||
// Here are all the lines of your table.
|
||||
reportData.forEach {
|
||||
val (productId, productName, sellsAmount, sellsPrice) = it
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
TableCell(text = productId.toString(), weight = column1Weight)
|
||||
TableCell(text = sellsAmount.toString(), weight = column1Weight)
|
||||
TableCell(text = sellsPrice.toString(), weight = column1Weight)
|
||||
}
|
||||
}
|
||||
/* Row(Modifier.fillMaxWidth()) {
|
||||
TableCell(text = "Общая цена: " + reportData.map { x -> x.sellsPrice}.sum().toString(), weight = column2Weight)
|
||||
}*/
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package com.example.myapplication.database.entities.composeui
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.myapplication.api.item.RestItemRepository
|
||||
import com.example.myapplication.api.report.ReportRemote
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class ReportViewModel(private val itemRespository: RestItemRepository): ViewModel() {
|
||||
var reportPageUiState by mutableStateOf(ReportPageUiState())
|
||||
private set
|
||||
|
||||
var reportResultPageUiState by mutableStateOf(ReportResultPageUiState())
|
||||
private set
|
||||
|
||||
fun onUpdate(reportDetails: ReportDetails) {
|
||||
reportPageUiState = ReportPageUiState(reportDetails = reportDetails, isEntryValid = validateInput(reportDetails))
|
||||
}
|
||||
|
||||
private fun validateInput(uiState: ReportDetails = reportPageUiState.reportDetails): Boolean {
|
||||
Log.d("validateInput", uiState.endDate.toString())
|
||||
return with(uiState) {
|
||||
startDate != Date(0) && endDate != Date(0) && startDate < endDate
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReport() {
|
||||
val dateFormatter = SimpleDateFormat("yyyy", Locale.getDefault())
|
||||
|
||||
// Преобразование даты в строку в формате "yyyy"
|
||||
val startDateQueryParam = dateFormatter.format(reportPageUiState.reportDetails.startDate)
|
||||
val endDateQueryParam = dateFormatter.format(reportPageUiState.reportDetails.endDate)
|
||||
|
||||
// Вызов репозитория с отформатированными датами
|
||||
val res = itemRespository.getReport(startDateQueryParam, endDateQueryParam)
|
||||
reportResultPageUiState = ReportResultPageUiState(res)
|
||||
}
|
||||
}
|
||||
|
||||
data class ReportDetails(
|
||||
val startDate: Date = Date(0),
|
||||
val endDate: Date = Date(0)
|
||||
)
|
||||
|
||||
data class ReportPageUiState(
|
||||
val reportDetails: ReportDetails = ReportDetails(),
|
||||
val isEntryValid: Boolean = false
|
||||
)
|
||||
|
||||
data class ReportResultPageUiState(
|
||||
var resReport: List<ReportRemote> = emptyList()
|
||||
)
|
@ -285,7 +285,8 @@ fun UserProfile(
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
createPdfFile(context = context, fileName = "отчет.pdf")
|
||||
//createPdfFile(context = context, fileName = "отчет.pdf")
|
||||
navController?.navigate(Screen.Report.route)
|
||||
Log.d("context", "${context}")
|
||||
}
|
||||
},
|
||||
|
@ -6,6 +6,9 @@
|
||||
<string name="Rent_view_title">Аренда</string>
|
||||
<string name="Bike_name">Название</string>
|
||||
<string name="Bike_description">Описание</string>
|
||||
<string name="report">Отчет</string>
|
||||
<string name="startDate">Дата начала</string>
|
||||
<string name="endDate">Дата конца</string>
|
||||
<string name="Bike_image">Изображение</string>
|
||||
<string name="Cart_title">Подтверждение</string>
|
||||
<string name="Rent_title">Аренда</string>
|
||||
|
61
server/router.js
Normal file
61
server/router.js
Normal file
@ -0,0 +1,61 @@
|
||||
const data = require('./data.json');
|
||||
|
||||
module.exports = (req, res, next) => {
|
||||
if (req.url.startsWith('/search') && req.method === 'GET') {
|
||||
try {
|
||||
const searchText = req.query.name;
|
||||
const searched = data.products.filter(
|
||||
(product) =>
|
||||
(product.name.toLowerCase()).includes(searchText.toLowerCase())
|
||||
);
|
||||
return res.json(searched);
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error);
|
||||
res.status(500).json({ message: 'Internal Server Error' });
|
||||
}
|
||||
} else if (req.url.startsWith('/report') && req.method === 'GET') {
|
||||
try {
|
||||
// Добавляем фильтрацию по полю dateTime
|
||||
const fromDate = req.query.fromDate;
|
||||
const toDate = req.query.toDate;
|
||||
const filteredData = filterByDateTime(data.items, fromDate, toDate);
|
||||
|
||||
// Возвращаем отфильтрованные элементы
|
||||
const reportData = generateReport(filteredData);
|
||||
res.json(reportData);
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error);
|
||||
return res.status(500).json({ message: 'Internal Server Error' });
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
// Функция generateReport для примера
|
||||
function generateReport(items) {
|
||||
// Ваш код для генерации отчета
|
||||
return items.map((item) => ({
|
||||
id: item.id,
|
||||
dateTime: item.dateTime,
|
||||
weight: item.weight,
|
||||
maxCount: item.maxCount,
|
||||
bikeId: item.bikeId
|
||||
|
||||
// Другие поля отчета, которые вам нужны
|
||||
}));
|
||||
}
|
||||
|
||||
// Функция для фильтрации по полю dateTime
|
||||
function filterByDateTime(items, fromDate, toDate) {
|
||||
if (!fromDate && !toDate) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const filteredItems = items.filter((item) => {
|
||||
const itemDate = item.dateTime;
|
||||
return (!fromDate || itemDate >= fromDate) && (!toDate || itemDate <= toDate);
|
||||
});
|
||||
|
||||
return filteredItems;
|
||||
}
|
Loading…
Reference in New Issue
Block a user