add offline mode
parent
d31bce2f89
commit
0074e651d0
|
@ -10,6 +10,14 @@
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="MainActivity">
|
<SelectionState runConfigName="MainActivity">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2024-07-09T21:11:01.409507Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/ernestlitvinenko/.android/avd/Small_Phone_API_28.avd" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
|
id("com.google.gms.google-services")
|
||||||
alias(libs.plugins.jetbrains.kotlin.android)
|
alias(libs.plugins.jetbrains.kotlin.android)
|
||||||
alias(libs.plugins.kotlinx.serialization)
|
alias(libs.plugins.kotlinx.serialization)
|
||||||
|
id("com.apollographql.apollo3").version("3.8.3")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -50,8 +52,13 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
apollo {
|
||||||
|
service("service") {
|
||||||
|
packageName.set("com.example.mpdriver")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
|
@ -76,4 +83,8 @@ dependencies {
|
||||||
implementation(libs.kotlinx.datetime)
|
implementation(libs.kotlinx.datetime)
|
||||||
implementation(libs.yandex.maps)
|
implementation(libs.yandex.maps)
|
||||||
implementation(libs.kotlin.coroutines)
|
implementation(libs.kotlin.coroutines)
|
||||||
|
implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
|
||||||
|
implementation("com.apollographql.apollo3:apollo-runtime:3.8.3")
|
||||||
|
implementation("ru.gildor.coroutines:kotlin-coroutines-okhttp:1.0")
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
query fetchApplicationData ($userID: String!) {
|
||||||
|
tasks(userId: $userID) {
|
||||||
|
id
|
||||||
|
startPln
|
||||||
|
endPln
|
||||||
|
startFact
|
||||||
|
endFact
|
||||||
|
status
|
||||||
|
taskType
|
||||||
|
text
|
||||||
|
|
||||||
|
events {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
subtasks {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
route {
|
||||||
|
temperatureProperty
|
||||||
|
name
|
||||||
|
|
||||||
|
trailer {
|
||||||
|
gost
|
||||||
|
}
|
||||||
|
|
||||||
|
truck {
|
||||||
|
gost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subtasks(userId: $userID) {
|
||||||
|
id
|
||||||
|
startPln
|
||||||
|
endPln
|
||||||
|
startFact
|
||||||
|
endFact
|
||||||
|
status
|
||||||
|
taskType
|
||||||
|
text
|
||||||
|
station {
|
||||||
|
name
|
||||||
|
location {
|
||||||
|
lat
|
||||||
|
lon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notes(userId: $userID) {
|
||||||
|
id
|
||||||
|
taskId
|
||||||
|
noteStatus
|
||||||
|
tip
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
query GetActiveSubtaskID($userID: String!) {
|
||||||
|
task(userId: $userID, isActive: true) {
|
||||||
|
activeSubtask {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
query GetActiveTaskId($userID: String!) {
|
||||||
|
task(isActive: true, userId: $userID) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
completedTasks: tasks(userId: $userID, isCompleted: true) {
|
||||||
|
id
|
||||||
|
startPln
|
||||||
|
}
|
||||||
|
|
||||||
|
plannedTasks: tasks(userId: $userID, isPlanned: true) {
|
||||||
|
id,
|
||||||
|
startPln
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
query GetPlannedTasksIDs($userID: String!) {
|
||||||
|
tasks(userId: $userID, isPlanned:true) {
|
||||||
|
id
|
||||||
|
subtasks {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
query GetSubtaskByID($userID: String!, $subtaskID: String!) {
|
||||||
|
subtask(userId: $userID, subtaskId: $subtaskID) {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
startPln
|
||||||
|
endPln
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
query GetTaskById($taskID: String!, $userID: String!) {
|
||||||
|
task(userId: $userID, taskId: $taskID) {
|
||||||
|
status
|
||||||
|
route {
|
||||||
|
name
|
||||||
|
temperatureProperty
|
||||||
|
truck {
|
||||||
|
gost
|
||||||
|
}
|
||||||
|
trailer {
|
||||||
|
gost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
text
|
||||||
|
startPln
|
||||||
|
endPln
|
||||||
|
startFact
|
||||||
|
endFact
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
query GetTasksFeedScreen($userID: String!){
|
||||||
|
tasks(userId: $userID) {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,453 @@
|
||||||
|
type Query {
|
||||||
|
tasks(userId: String!, isPlanned: Boolean = false, isCompleted: Boolean = false): [AppTaskQL!]!
|
||||||
|
|
||||||
|
task(userId: String!, taskId: String = null, isActive: Boolean = null): AppTaskQL
|
||||||
|
|
||||||
|
countPlannedTasks(userId: String!): Int!
|
||||||
|
|
||||||
|
countCompletedTasks(userId: String!): Int!
|
||||||
|
|
||||||
|
notes(userId: String!): [AppNoteQL!]!
|
||||||
|
|
||||||
|
subtask(userId: String!, subtaskId: String!): SubtaskQL
|
||||||
|
|
||||||
|
subtasks(userId: String!): [SubtaskQL!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppTaskQL {
|
||||||
|
activeSubtask: SubtaskQL
|
||||||
|
|
||||||
|
id: String!
|
||||||
|
|
||||||
|
profileId: Int!
|
||||||
|
|
||||||
|
startPln: DateTime!
|
||||||
|
|
||||||
|
endPln: DateTime!
|
||||||
|
|
||||||
|
startFact: DateTime
|
||||||
|
|
||||||
|
endFact: DateTime
|
||||||
|
|
||||||
|
status: StatusEnumQl!
|
||||||
|
|
||||||
|
taskType: String!
|
||||||
|
|
||||||
|
text: String!
|
||||||
|
|
||||||
|
events: [AppEventQL!]!
|
||||||
|
|
||||||
|
subtasks: [SubtaskQL!]!
|
||||||
|
|
||||||
|
route: AppRouteQL!
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubtaskQL {
|
||||||
|
id: String!
|
||||||
|
|
||||||
|
startPln: DateTime!
|
||||||
|
|
||||||
|
endPln: DateTime!
|
||||||
|
|
||||||
|
startFact: DateTime
|
||||||
|
|
||||||
|
endFact: DateTime
|
||||||
|
|
||||||
|
status: StatusEnumQl!
|
||||||
|
|
||||||
|
taskType: String!
|
||||||
|
|
||||||
|
text: String!
|
||||||
|
|
||||||
|
station: MSTQL
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
|
||||||
|
"""
|
||||||
|
scalar String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Date with time (isoformat)
|
||||||
|
"""
|
||||||
|
scalar DateTime
|
||||||
|
|
||||||
|
enum StatusEnumQl {
|
||||||
|
CANCELLED
|
||||||
|
|
||||||
|
IN_PROGRESS
|
||||||
|
|
||||||
|
COMPLETED
|
||||||
|
|
||||||
|
NOT_DEFINED
|
||||||
|
}
|
||||||
|
|
||||||
|
type MSTQL {
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
location: LocationQL!
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocationQL {
|
||||||
|
lat: Float!
|
||||||
|
|
||||||
|
lon: Float!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).
|
||||||
|
"""
|
||||||
|
scalar Float
|
||||||
|
|
||||||
|
"""
|
||||||
|
The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
|
||||||
|
"""
|
||||||
|
scalar Int
|
||||||
|
|
||||||
|
type AppEventQL {
|
||||||
|
id: String!
|
||||||
|
|
||||||
|
type: String!
|
||||||
|
|
||||||
|
text: String!
|
||||||
|
|
||||||
|
eventDatetime: DateTime!
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppRouteQL {
|
||||||
|
temperatureProperty: MarshTemperaturePropertyQL!
|
||||||
|
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
trailer: TRSQL
|
||||||
|
|
||||||
|
truck: TRSQL
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MarshTemperaturePropertyQL {
|
||||||
|
HOT
|
||||||
|
|
||||||
|
COLD
|
||||||
|
|
||||||
|
UNDEFINED
|
||||||
|
}
|
||||||
|
|
||||||
|
type TRSQL {
|
||||||
|
gost: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The `Boolean` scalar type represents `true` or `false`.
|
||||||
|
"""
|
||||||
|
scalar Boolean
|
||||||
|
|
||||||
|
type AppNoteQL {
|
||||||
|
id: String!
|
||||||
|
|
||||||
|
userId: String!
|
||||||
|
|
||||||
|
taskId: String!
|
||||||
|
|
||||||
|
noteStatus: Int!
|
||||||
|
|
||||||
|
tip: Int!
|
||||||
|
|
||||||
|
text: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.
|
||||||
|
"""
|
||||||
|
type __Schema {
|
||||||
|
description: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
A list of all types supported by this server.
|
||||||
|
"""
|
||||||
|
types: [__Type!]!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The type that query operations will be rooted at.
|
||||||
|
"""
|
||||||
|
queryType: __Type!
|
||||||
|
|
||||||
|
"""
|
||||||
|
If this server supports mutation, the type that mutation operations will be rooted at.
|
||||||
|
"""
|
||||||
|
mutationType: __Type
|
||||||
|
|
||||||
|
"""
|
||||||
|
If this server support subscription, the type that subscription operations will be rooted at.
|
||||||
|
"""
|
||||||
|
subscriptionType: __Type
|
||||||
|
|
||||||
|
"""
|
||||||
|
A list of all directives supported by this server.
|
||||||
|
"""
|
||||||
|
directives: [__Directive!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
|
||||||
|
|
||||||
|
Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByURL`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
|
||||||
|
"""
|
||||||
|
type __Type {
|
||||||
|
kind: __TypeKind!
|
||||||
|
|
||||||
|
name: String
|
||||||
|
|
||||||
|
description: String
|
||||||
|
|
||||||
|
specifiedByURL: String
|
||||||
|
|
||||||
|
fields(includeDeprecated: Boolean = false): [__Field!]
|
||||||
|
|
||||||
|
interfaces: [__Type!]
|
||||||
|
|
||||||
|
possibleTypes: [__Type!]
|
||||||
|
|
||||||
|
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
|
||||||
|
|
||||||
|
inputFields(includeDeprecated: Boolean = false): [__InputValue!]
|
||||||
|
|
||||||
|
ofType: __Type
|
||||||
|
|
||||||
|
isOneOf: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
An enum describing what kind of type a given `__Type` is.
|
||||||
|
"""
|
||||||
|
enum __TypeKind {
|
||||||
|
"""
|
||||||
|
Indicates this type is a scalar.
|
||||||
|
"""
|
||||||
|
SCALAR
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates this type is an object. `fields` and `interfaces` are valid fields.
|
||||||
|
"""
|
||||||
|
OBJECT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates this type is an interface. `fields`, `interfaces`, and `possibleTypes` are valid fields.
|
||||||
|
"""
|
||||||
|
INTERFACE
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates this type is a union. `possibleTypes` is a valid field.
|
||||||
|
"""
|
||||||
|
UNION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates this type is an enum. `enumValues` is a valid field.
|
||||||
|
"""
|
||||||
|
ENUM
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates this type is an input object. `inputFields` is a valid field.
|
||||||
|
"""
|
||||||
|
INPUT_OBJECT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates this type is a list. `ofType` is a valid field.
|
||||||
|
"""
|
||||||
|
LIST
|
||||||
|
|
||||||
|
"""
|
||||||
|
Indicates this type is a non-null. `ofType` is a valid field.
|
||||||
|
"""
|
||||||
|
NON_NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.
|
||||||
|
"""
|
||||||
|
type __Field {
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
description: String
|
||||||
|
|
||||||
|
args(includeDeprecated: Boolean = false): [__InputValue!]!
|
||||||
|
|
||||||
|
type: __Type!
|
||||||
|
|
||||||
|
isDeprecated: Boolean!
|
||||||
|
|
||||||
|
deprecationReason: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.
|
||||||
|
"""
|
||||||
|
type __InputValue {
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
description: String
|
||||||
|
|
||||||
|
type: __Type!
|
||||||
|
|
||||||
|
"""
|
||||||
|
A GraphQL-formatted string representing the default value for this input value.
|
||||||
|
"""
|
||||||
|
defaultValue: String
|
||||||
|
|
||||||
|
isDeprecated: Boolean!
|
||||||
|
|
||||||
|
deprecationReason: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.
|
||||||
|
"""
|
||||||
|
type __EnumValue {
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
description: String
|
||||||
|
|
||||||
|
isDeprecated: Boolean!
|
||||||
|
|
||||||
|
deprecationReason: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
|
||||||
|
|
||||||
|
In some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.
|
||||||
|
"""
|
||||||
|
type __Directive {
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
description: String
|
||||||
|
|
||||||
|
isRepeatable: Boolean!
|
||||||
|
|
||||||
|
locations: [__DirectiveLocation!]!
|
||||||
|
|
||||||
|
args(includeDeprecated: Boolean = false): [__InputValue!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.
|
||||||
|
"""
|
||||||
|
enum __DirectiveLocation {
|
||||||
|
"""
|
||||||
|
Location adjacent to a query operation.
|
||||||
|
"""
|
||||||
|
QUERY
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a mutation operation.
|
||||||
|
"""
|
||||||
|
MUTATION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a subscription operation.
|
||||||
|
"""
|
||||||
|
SUBSCRIPTION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a field.
|
||||||
|
"""
|
||||||
|
FIELD
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a fragment definition.
|
||||||
|
"""
|
||||||
|
FRAGMENT_DEFINITION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a fragment spread.
|
||||||
|
"""
|
||||||
|
FRAGMENT_SPREAD
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an inline fragment.
|
||||||
|
"""
|
||||||
|
INLINE_FRAGMENT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a variable definition.
|
||||||
|
"""
|
||||||
|
VARIABLE_DEFINITION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a schema definition.
|
||||||
|
"""
|
||||||
|
SCHEMA
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a scalar definition.
|
||||||
|
"""
|
||||||
|
SCALAR
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an object type definition.
|
||||||
|
"""
|
||||||
|
OBJECT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a field definition.
|
||||||
|
"""
|
||||||
|
FIELD_DEFINITION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an argument definition.
|
||||||
|
"""
|
||||||
|
ARGUMENT_DEFINITION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an interface definition.
|
||||||
|
"""
|
||||||
|
INTERFACE
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to a union definition.
|
||||||
|
"""
|
||||||
|
UNION
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an enum definition.
|
||||||
|
"""
|
||||||
|
ENUM
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an enum value definition.
|
||||||
|
"""
|
||||||
|
ENUM_VALUE
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an input object type definition.
|
||||||
|
"""
|
||||||
|
INPUT_OBJECT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Location adjacent to an input object field definition.
|
||||||
|
"""
|
||||||
|
INPUT_FIELD_DEFINITION
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Directs the executor to include this field or fragment only when the `if` argument is true.
|
||||||
|
"""
|
||||||
|
directive @include ("Included when true." if: Boolean!) on FIELD|FRAGMENT_SPREAD|INLINE_FRAGMENT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Directs the executor to skip this field or fragment when the `if` argument is true.
|
||||||
|
"""
|
||||||
|
directive @skip ("Skipped when true." if: Boolean!) on FIELD|FRAGMENT_SPREAD|INLINE_FRAGMENT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Marks an element of a GraphQL schema as no longer supported.
|
||||||
|
"""
|
||||||
|
directive @deprecated ("Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https:\/\/commonmark.org\/)." reason: String = "No longer supported") on FIELD_DEFINITION|ARGUMENT_DEFINITION|INPUT_FIELD_DEFINITION|ENUM_VALUE
|
||||||
|
|
||||||
|
"""
|
||||||
|
Exposes a URL that specifies the behaviour of this scalar.
|
||||||
|
"""
|
||||||
|
directive @specifiedBy ("The URL that specifies the behaviour of this scalar." url: String!) on SCALAR
|
||||||
|
|
||||||
|
schema {
|
||||||
|
query: Query
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package com.example.mpdriver
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
@ -22,6 +23,7 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -30,6 +32,7 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.sharp.Settings
|
import androidx.compose.material.icons.sharp.Settings
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
@ -37,9 +40,11 @@ import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
@ -49,6 +54,7 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
@ -58,9 +64,16 @@ import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import com.example.mpdriver.api.Api
|
import com.example.mpdriver.api.Api
|
||||||
|
import com.example.mpdriver.api.TaskApi
|
||||||
|
import com.example.mpdriver.api.apolloClient
|
||||||
|
import com.example.mpdriver.api.fetchAppDataToDB
|
||||||
|
|
||||||
|
|
||||||
|
import com.example.mpdriver.api.toJson
|
||||||
import com.example.mpdriver.recievers.TimeTickReciever
|
import com.example.mpdriver.recievers.TimeTickReciever
|
||||||
import com.example.mpdriver.screens.Feed
|
import com.example.mpdriver.screens.Feed
|
||||||
import com.example.mpdriver.screens.MapScreen
|
import com.example.mpdriver.screens.MapScreen
|
||||||
|
import com.example.mpdriver.screens.NoteScreen
|
||||||
import com.example.mpdriver.screens.PhoneCodeInputScreen
|
import com.example.mpdriver.screens.PhoneCodeInputScreen
|
||||||
import com.example.mpdriver.screens.PhoneInputScreen
|
import com.example.mpdriver.screens.PhoneInputScreen
|
||||||
import com.example.mpdriver.screens.SubtaskScreen
|
import com.example.mpdriver.screens.SubtaskScreen
|
||||||
|
@ -68,6 +81,8 @@ import com.example.mpdriver.screens.TasksList
|
||||||
import com.example.mpdriver.storage.Database
|
import com.example.mpdriver.storage.Database
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import com.yandex.mapkit.MapKitFactory
|
import com.yandex.mapkit.MapKitFactory
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.Dispatcher
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@ -79,6 +94,8 @@ class MainActivity : ComponentActivity() {
|
||||||
MapKitFactory.setApiKey("f4385b18-0740-454a-a71f-d20da7e8fc3b")
|
MapKitFactory.setApiKey("f4385b18-0740-454a-a71f-d20da7e8fc3b")
|
||||||
MapKitFactory.initialize(this)
|
MapKitFactory.initialize(this)
|
||||||
val rootDir = MMKV.initialize(this)
|
val rootDir = MMKV.initialize(this)
|
||||||
|
Log.d("MMKV.KEYS", Database.allKeys!!.joinToString())
|
||||||
|
|
||||||
println("mmkv root: $rootDir")
|
println("mmkv root: $rootDir")
|
||||||
registerReceiver(timeTickReciever, IntentFilter(Intent.ACTION_TIME_TICK))
|
registerReceiver(timeTickReciever, IntentFilter(Intent.ACTION_TIME_TICK))
|
||||||
|
|
||||||
|
@ -123,7 +140,32 @@ fun MainNavigator() {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var loadingData by remember {
|
||||||
|
mutableStateOf(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val bottomNavController = rememberNavController()
|
val bottomNavController = rememberNavController()
|
||||||
|
|
||||||
|
val api = TaskApi(LocalContext.current)
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
api.send_task_status_chains(onResponse = {
|
||||||
|
Database.dropUpdates()
|
||||||
|
})
|
||||||
|
apolloClient.fetchAppDataToDB()
|
||||||
|
loadingData = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadingData)
|
||||||
|
return Row(Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Column(Modifier.fillMaxWidth().padding(horizontal = 16.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||||
|
Text( text = "Происходит обновление даннных, Пожалуйста, подождите.", fontSize = 16.sp, textAlign = TextAlign.Center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
Header(
|
Header(
|
||||||
|
@ -132,9 +174,11 @@ fun MainNavigator() {
|
||||||
backLink = backLink
|
backLink = backLink
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = { Footer(
|
bottomBar = {
|
||||||
hostController = bottomNavController
|
Footer(
|
||||||
) },
|
hostController = bottomNavController
|
||||||
|
)
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,14 +189,19 @@ fun MainNavigator() {
|
||||||
) {
|
) {
|
||||||
composable("feed",
|
composable("feed",
|
||||||
exitTransition = {
|
exitTransition = {
|
||||||
slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Left, tween(200))
|
slideOutOfContainer(
|
||||||
|
AnimatedContentTransitionScope.SlideDirection.Left,
|
||||||
|
tween(200)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
backLink = false
|
backLink = false
|
||||||
headerTitle = "Лента"
|
headerTitle = "Лента"
|
||||||
Box(modifier = Modifier
|
Box(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.fillMaxHeight()) {
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
Feed(hostController = bottomNavController)
|
Feed(hostController = bottomNavController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,15 +209,18 @@ fun MainNavigator() {
|
||||||
|
|
||||||
enterTransition = {
|
enterTransition = {
|
||||||
slideIntoContainer(
|
slideIntoContainer(
|
||||||
AnimatedContentTransitionScope.SlideDirection.Left, tween(200))
|
AnimatedContentTransitionScope.SlideDirection.Left, tween(200)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
) {
|
) {
|
||||||
backLink = true
|
backLink = true
|
||||||
headerTitle = "Задачи"
|
headerTitle = "Задачи"
|
||||||
Box(modifier = Modifier
|
Box(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.fillMaxHeight()) {
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
TasksList(hostController = bottomNavController)
|
TasksList(hostController = bottomNavController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +228,11 @@ fun MainNavigator() {
|
||||||
backLink = false
|
backLink = false
|
||||||
headerTitle = "События"
|
headerTitle = "События"
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
Text(text = "События")
|
Text(text = "События")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,31 +240,43 @@ fun MainNavigator() {
|
||||||
composable("settings") {
|
composable("settings") {
|
||||||
backLink = false
|
backLink = false
|
||||||
headerTitle = "Настройки"
|
headerTitle = "Настройки"
|
||||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
Text(text = "Profile")
|
Text(text = "Profile")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composable("notifications") {
|
composable("notifications") {
|
||||||
backLink = false
|
backLink = false
|
||||||
headerTitle = "Уведомления"
|
headerTitle = "Уведомления"
|
||||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
Box(
|
||||||
Text(text = "Notifications")
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
|
NoteScreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composable("chat") {
|
composable("chat") {
|
||||||
backLink = false
|
backLink = false
|
||||||
headerTitle = "Чат"
|
headerTitle = "Чат"
|
||||||
Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()) {
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
Text(text = "Chat")
|
Text(text = "Chat")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composable("map",
|
composable("map",
|
||||||
enterTransition = {
|
enterTransition = {
|
||||||
fadeIn()
|
fadeIn()
|
||||||
},
|
},
|
||||||
exitTransition = {
|
exitTransition = {
|
||||||
ExitTransition.None
|
ExitTransition.None
|
||||||
}) {
|
}) {
|
||||||
backLink = false
|
backLink = false
|
||||||
headerTitle = "Карта"
|
headerTitle = "Карта"
|
||||||
MapScreen()
|
MapScreen()
|
||||||
|
@ -218,20 +286,23 @@ fun MainNavigator() {
|
||||||
}),
|
}),
|
||||||
enterTransition = {
|
enterTransition = {
|
||||||
slideIntoContainer(
|
slideIntoContainer(
|
||||||
AnimatedContentTransitionScope.SlideDirection.Left, tween(200))
|
AnimatedContentTransitionScope.SlideDirection.Left, tween(200)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) {bse ->
|
) { bse ->
|
||||||
bse.arguments?.let {args ->
|
bse.arguments?.let { args ->
|
||||||
headerTitle = "Детали задачи"
|
headerTitle = "Детали задачи"
|
||||||
backLink = true
|
backLink = true
|
||||||
Box(modifier = Modifier
|
Box(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.fillMaxHeight()){
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
SubtaskScreen(args.getLong("taskId"))
|
SubtaskScreen(args.getLong("taskId"))
|
||||||
}
|
}
|
||||||
return@composable
|
return@composable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,14 +371,19 @@ data class FooterNavMenuLabel(
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Footer(modifier: Modifier = Modifier,
|
fun Footer(
|
||||||
hostController: NavHostController
|
modifier: Modifier = Modifier,
|
||||||
|
hostController: NavHostController
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val menuItems = mapOf<String, FooterNavMenuLabel>(
|
val menuItems = mapOf<String, FooterNavMenuLabel>(
|
||||||
"feed" to FooterNavMenuLabel("Лента", R.drawable.home_default, R.drawable.home),
|
"feed" to FooterNavMenuLabel("Лента", R.drawable.home_default, R.drawable.home),
|
||||||
"events" to FooterNavMenuLabel("События", R.drawable.calendar_default, R.drawable.calendar),
|
"events" to FooterNavMenuLabel("События", R.drawable.calendar_default, R.drawable.calendar),
|
||||||
"notifications" to FooterNavMenuLabel("Уведомления", R.drawable.bell_default, R.drawable.bell),
|
"notifications" to FooterNavMenuLabel(
|
||||||
|
"Уведомления",
|
||||||
|
R.drawable.bell_default,
|
||||||
|
R.drawable.bell
|
||||||
|
),
|
||||||
"chat" to FooterNavMenuLabel("Чат", R.drawable.chat_default, R.drawable.chat),
|
"chat" to FooterNavMenuLabel("Чат", R.drawable.chat_default, R.drawable.chat),
|
||||||
"map" to FooterNavMenuLabel("Карта", R.drawable.location_default, R.drawable.location)
|
"map" to FooterNavMenuLabel("Карта", R.drawable.location_default, R.drawable.location)
|
||||||
)
|
)
|
||||||
|
@ -337,17 +413,23 @@ fun Footer(modifier: Modifier = Modifier,
|
||||||
onClick = {
|
onClick = {
|
||||||
activeRoute = it.key
|
activeRoute = it.key
|
||||||
hostController.navigate(it.key)
|
hostController.navigate(it.key)
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.textButtonColors(
|
colors = ButtonDefaults.textButtonColors(
|
||||||
contentColor = Color.Black,
|
contentColor = Color.Black,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Column (horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
if (activeRoute== it.key) {
|
if (activeRoute == it.key) {
|
||||||
Image(painter = painterResource(id = it.value.imageActive), contentDescription = "")
|
Image(
|
||||||
|
painter = painterResource(id = it.value.imageActive),
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
Text(text = it.value.title, fontSize = 11.sp, color = Color.Black)
|
Text(text = it.value.title, fontSize = 11.sp, color = Color.Black)
|
||||||
} else {
|
} else {
|
||||||
Image(painter = painterResource(id = it.value.defaultImage), contentDescription = "")
|
Image(
|
||||||
|
painter = painterResource(id = it.value.defaultImage),
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
Text(text = it.value.title, fontSize = 11.sp, color = Color.Gray)
|
Text(text = it.value.title, fontSize = 11.sp, color = Color.Gray)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import okhttp3.Response
|
||||||
import okhttp3.internal.EMPTY_REQUEST
|
import okhttp3.internal.EMPTY_REQUEST
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
data class Langs(
|
data class Langs(
|
||||||
|
@ -41,11 +42,10 @@ inline fun <reified T> Response.parseList(): List<T> {
|
||||||
|
|
||||||
|
|
||||||
open class Api(val ctx: Context) {
|
open class Api(val ctx: Context) {
|
||||||
val kv = MMKV.defaultMMKV()
|
|
||||||
|
|
||||||
val clientCheckAuth = OkHttpClient.Builder().build()
|
val clientCheckAuth = OkHttpClient.Builder().build()
|
||||||
|
|
||||||
val client = OkHttpClient.Builder()
|
val client = OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30 ,TimeUnit.SECONDS)
|
||||||
.addInterceptor { chain ->
|
.addInterceptor { chain ->
|
||||||
val req = chain.request().newBuilder()
|
val req = chain.request().newBuilder()
|
||||||
.addHeader("Authorization", "Bearer ${Database.access_token}")
|
.addHeader("Authorization", "Bearer ${Database.access_token}")
|
||||||
|
@ -55,7 +55,7 @@ open class Api(val ctx: Context) {
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
||||||
val BASE_URL = "http://147.45.107.119:8000/api/v1"
|
val BASE_URL = "http://192.168.0.101:8000/api/v1"
|
||||||
|
|
||||||
fun performRequest(clientReq: Request, errorHandler: (Exception) -> Unit = {}, handler: (Response) -> Unit) {
|
fun performRequest(clientReq: Request, errorHandler: (Exception) -> Unit = {}, handler: (Response) -> Unit) {
|
||||||
client.newCall(clientReq).enqueue(object : Callback {
|
client.newCall(clientReq).enqueue(object : Callback {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package com.example.mpdriver.api
|
package com.example.mpdriver.api
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.provider.ContactsContract.Data
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.example.mpdriver.storage.CreateUpdateTaskData
|
||||||
import com.example.mpdriver.storage.Database
|
import com.example.mpdriver.storage.Database
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -9,9 +11,15 @@ import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import okhttp3.Response
|
||||||
|
import ru.gildor.coroutines.okhttp.await
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.math.log
|
||||||
|
|
||||||
|
|
||||||
data class SetTaskStatusRequest(
|
data class SetTaskStatusRequest(
|
||||||
|
@ -20,80 +28,110 @@ data class SetTaskStatusRequest(
|
||||||
|
|
||||||
data class TaskResponse(
|
data class TaskResponse(
|
||||||
|
|
||||||
@SerializedName("id" ) var id: Long? = null,
|
@SerializedName("id") var id: Long? = null,
|
||||||
@SerializedName("startPln" ) var startPln: String? = null,
|
@SerializedName("startPln") var startPln: String? = null,
|
||||||
@SerializedName("endPln" ) var endPln: String? = null,
|
@SerializedName("endPln") var endPln: String? = null,
|
||||||
@SerializedName("startFact" ) var startFact: String? = null,
|
@SerializedName("startFact") var startFact: String? = null,
|
||||||
@SerializedName("endFact" ) var endFact: String? = null,
|
@SerializedName("endFact") var endFact: String? = null,
|
||||||
@SerializedName("status" ) var status: String? = null,
|
@SerializedName("status") var status: String? = null,
|
||||||
@SerializedName("taskType" ) var taskType: String? = null,
|
@SerializedName("taskType") var taskType: String? = null,
|
||||||
@SerializedName("text" ) var text: String? = null,
|
@SerializedName("text") var text: String? = null,
|
||||||
@SerializedName("events" ) var events: ArrayList<Events> = arrayListOf(),
|
@SerializedName("events") var events: ArrayList<Events> = arrayListOf(),
|
||||||
@SerializedName("subtasks" ) var subtasks: ArrayList<Subtasks> = arrayListOf(),
|
@SerializedName("subtasks") var subtasks: ArrayList<Subtasks> = arrayListOf(),
|
||||||
@SerializedName("route" ) var route: Route? = Route()
|
@SerializedName("route") var route: Route? = Route()
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Events (
|
data class Events(
|
||||||
|
|
||||||
@SerializedName("id" ) var id : Long? = null,
|
@SerializedName("id") var id: Long? = null,
|
||||||
@SerializedName("type" ) var type : String? = null,
|
@SerializedName("type") var type: String? = null,
|
||||||
@SerializedName("text" ) var text : String? = null,
|
@SerializedName("text") var text: String? = null,
|
||||||
@SerializedName("eventDatetime" ) var eventDatetime : String? = null
|
@SerializedName("eventDatetime") var eventDatetime: String? = null
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Location (
|
data class Location(
|
||||||
|
|
||||||
@SerializedName("lat" ) var lat : Double? = null,
|
@SerializedName("lat") var lat: Double? = null,
|
||||||
@SerializedName("lon" ) var lon : Double? = null
|
@SerializedName("lon") var lon: Double? = null
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Station (
|
data class Station(
|
||||||
|
|
||||||
@SerializedName("id" ) var id : Long? = null,
|
@SerializedName("id") var id: Long? = null,
|
||||||
@SerializedName("name" ) var name : String? = null,
|
@SerializedName("name") var name: String? = null,
|
||||||
@SerializedName("location" ) var location : Location? = Location()
|
@SerializedName("location") var location: Location? = Location()
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Subtasks (
|
data class Subtasks(
|
||||||
|
|
||||||
@SerializedName("id" ) var id : Long? = null,
|
@SerializedName("id") var id: Long? = null,
|
||||||
@SerializedName("parentId" ) var parentId : Long? = null,
|
@SerializedName("parentId") var parentId: Long? = null,
|
||||||
@SerializedName("startPln" ) var startPln : String? = null,
|
@SerializedName("startPln") var startPln: String? = null,
|
||||||
@SerializedName("endPln" ) var endPln : String? = null,
|
@SerializedName("endPln") var endPln: String? = null,
|
||||||
@SerializedName("startFact" ) var startFact : String? = null,
|
@SerializedName("startFact") var startFact: String? = null,
|
||||||
@SerializedName("endFact" ) var endFact : String? = null,
|
@SerializedName("endFact") var endFact: String? = null,
|
||||||
@SerializedName("status" ) var status : String? = null,
|
@SerializedName("status") var status: String? = null,
|
||||||
@SerializedName("taskType" ) var taskType : String? = null,
|
@SerializedName("taskType") var taskType: String? = null,
|
||||||
@SerializedName("text" ) var text : String? = null,
|
@SerializedName("text") var text: String? = null,
|
||||||
@SerializedName("station" ) var station : Station? = Station()
|
@SerializedName("station") var station: Station? = Station()
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class TRS (
|
data class TRS(
|
||||||
|
|
||||||
@SerializedName("id" ) var id : Long? = null,
|
@SerializedName("id") var id: Long? = null,
|
||||||
@SerializedName("gost" ) var gost : String? = null
|
@SerializedName("gost") var gost: String? = null
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Route (
|
data class Route(
|
||||||
|
|
||||||
@SerializedName("id" ) var id : Long? = null,
|
@SerializedName("id") var id: Long? = null,
|
||||||
@SerializedName("temperatureProperty" ) var temperatureProperty : Int? = null,
|
@SerializedName("temperatureProperty") var temperatureProperty: Int? = null,
|
||||||
@SerializedName("name" ) var name : String? = null,
|
@SerializedName("name") var name: String? = null,
|
||||||
@SerializedName("trailer" ) var trailer : TRS? = null,
|
@SerializedName("trailer") var trailer: TRS? = null,
|
||||||
@SerializedName("truck" ) var truck : TRS? = null
|
@SerializedName("truck") var truck: TRS? = null
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TaskApi(ctx: Context): Api(ctx) {
|
class TaskApi(ctx: Context) : Api(ctx) {
|
||||||
|
|
||||||
suspend fun getPlannedTasksApiCall(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
data class SendTaskStatusReq(
|
||||||
|
val data: List<CreateUpdateTaskData>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun send_task_status_chains(onResponse: (Response) -> Unit = {}, onFailure: () -> Unit = {}) {
|
||||||
|
val chainBody = SendTaskStatusReq(Database.updateTasks).toJson().toRequestBody()
|
||||||
|
val req = Request.Builder().url("$BASE_URL/tasks").post(chainBody).build()
|
||||||
|
client.newCall(req).enqueue(
|
||||||
|
object : Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
Log.d("taskAPI", "${e}")
|
||||||
|
onFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
Log.d("taskAPI", response.body.toString())
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
return onResponse(response)
|
||||||
|
}
|
||||||
|
return onFailure()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getPlannedTasksApiCall(
|
||||||
|
errorHandler: (Exception) -> Unit = {},
|
||||||
|
handler: (List<TaskResponse>) -> Unit
|
||||||
|
) {
|
||||||
val req = Request.Builder()
|
val req = Request.Builder()
|
||||||
.url("$BASE_URL/tasks/planned")
|
.url("$BASE_URL/tasks/planned")
|
||||||
.build()
|
.build()
|
||||||
|
@ -103,21 +141,24 @@ class TaskApi(ctx: Context): Api(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPlannedTasks(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
// fun getPlannedTasks(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
||||||
runBlocking {
|
// runBlocking {
|
||||||
launch(Dispatchers.IO) {
|
// launch(Dispatchers.IO) {
|
||||||
getPlannedTasksApiCall {
|
// getPlannedTasksApiCall {
|
||||||
println(Database.planned_tasks)
|
// println(Database.planned_tasks)
|
||||||
println(it)
|
// println(it)
|
||||||
Database.planned_tasks = it
|
// Database.planned_tasks = it
|
||||||
println(Database.planned_tasks)
|
// println(Database.planned_tasks)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
handler(Database.planned_tasks)
|
// handler(Database.planned_tasks)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
suspend fun getActiveTaskApiCall(errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
suspend fun getActiveTaskApiCall(
|
||||||
|
errorHandler: (Exception) -> Unit = {},
|
||||||
|
handler: (TaskResponse) -> Unit
|
||||||
|
) {
|
||||||
val req = Request.Builder()
|
val req = Request.Builder()
|
||||||
.url("$BASE_URL/tasks/active")
|
.url("$BASE_URL/tasks/active")
|
||||||
.build()
|
.build()
|
||||||
|
@ -126,21 +167,24 @@ class TaskApi(ctx: Context): Api(ctx) {
|
||||||
handler(it.parse<TaskResponse>())
|
handler(it.parse<TaskResponse>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun getActiveTask(errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
// fun getActiveTask(errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
||||||
runBlocking {
|
// runBlocking {
|
||||||
launch(Dispatchers.IO) {
|
// launch(Dispatchers.IO) {
|
||||||
getAllTasksForUser()
|
// getAllTasksForUser()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// val data = Database.tasks.filter { it.status == "InProgress" }
|
||||||
|
// if (data.count() >= 1) {
|
||||||
|
// return@runBlocking handler(data[0])
|
||||||
|
// }
|
||||||
|
// handler(TaskResponse())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
val data = Database.tasks.filter { it.status == "InProgress" }
|
fun getCompletedTask(
|
||||||
if (data.count() >= 1) {
|
errorHandler: (Exception) -> Unit = {},
|
||||||
return@runBlocking handler(data[0])
|
handler: (List<TaskResponse>) -> Unit
|
||||||
}
|
) {
|
||||||
handler(TaskResponse())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCompletedTask(errorHandler: (Exception)-> Unit = {}, handler: (List<TaskResponse>) -> Unit) {
|
|
||||||
val req = Request.Builder()
|
val req = Request.Builder()
|
||||||
.url("$BASE_URL/tasks/completed")
|
.url("$BASE_URL/tasks/completed")
|
||||||
.build()
|
.build()
|
||||||
|
@ -149,7 +193,12 @@ class TaskApi(ctx: Context): Api(ctx) {
|
||||||
handler(it.parseList<TaskResponse>())
|
handler(it.parseList<TaskResponse>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun getSubtasks(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (List<Subtasks>) -> Unit) {
|
|
||||||
|
fun getSubtasks(
|
||||||
|
taskId: Long,
|
||||||
|
errorHandler: (Exception) -> Unit = {},
|
||||||
|
handler: (List<Subtasks>) -> Unit
|
||||||
|
) {
|
||||||
val req = Request.Builder()
|
val req = Request.Builder()
|
||||||
.url("$BASE_URL/tasks/$taskId/subtasks")
|
.url("$BASE_URL/tasks/$taskId/subtasks")
|
||||||
.build()
|
.build()
|
||||||
|
@ -158,7 +207,12 @@ class TaskApi(ctx: Context): Api(ctx) {
|
||||||
handler(it.parseList<Subtasks>())
|
handler(it.parseList<Subtasks>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun getEvents(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (List<Events>) -> Unit) {
|
|
||||||
|
fun getEvents(
|
||||||
|
taskId: Long,
|
||||||
|
errorHandler: (Exception) -> Unit = {},
|
||||||
|
handler: (List<Events>) -> Unit
|
||||||
|
) {
|
||||||
val req = Request.Builder()
|
val req = Request.Builder()
|
||||||
.url("$BASE_URL/tasks/$taskId/events")
|
.url("$BASE_URL/tasks/$taskId/events")
|
||||||
.build()
|
.build()
|
||||||
|
@ -168,24 +222,24 @@ class TaskApi(ctx: Context): Api(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllTasksForUser() {
|
// fun getAllTasksForUser() {
|
||||||
val req = Request.Builder().url("$BASE_URL/tasks").build()
|
// val req = Request.Builder().url("$BASE_URL/tasks").build()
|
||||||
performRequest(req) {res ->
|
// performRequest(req) {res ->
|
||||||
val data = res.parseList<TaskResponse>()
|
// val data = res.parseList<TaskResponse>()
|
||||||
Database.tasks = data
|
// Database.tasks = data
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun setTaskStatusInProgress(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
// fun setTaskStatusInProgress(taskId: Long, errorHandler: (Exception)-> Unit = {}, handler: (TaskResponse) -> Unit) {
|
||||||
|
//
|
||||||
val body = SetTaskStatusRequest(taskId).toJson().toRequestBody("application/json".toMediaType())
|
// val body = SetTaskStatusRequest(taskId).toJson().toRequestBody("application/json".toMediaType())
|
||||||
|
//
|
||||||
val req = Request.Builder()
|
// val req = Request.Builder()
|
||||||
.url("$BASE_URL/tasks/active")
|
// .url("$BASE_URL/tasks/active")
|
||||||
.post(body)
|
// .post(body)
|
||||||
|
//
|
||||||
performRequest(req.build(), errorHandler) {
|
// performRequest(req.build(), errorHandler) {
|
||||||
handler(it.parse())
|
// handler(it.parse())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.example.mpdriver.api
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import com.apollographql.apollo3.ApolloClient
|
||||||
|
import com.apollographql.apollo3.network.http.DefaultHttpEngine
|
||||||
|
import com.example.mpdriver.FetchApplicationDataQuery
|
||||||
|
import com.example.mpdriver.storage.Database
|
||||||
|
import com.tencent.mmkv.MMKV
|
||||||
|
|
||||||
|
|
||||||
|
val apolloClient = ApolloClient.Builder().serverUrl("http://192.168.0.101:8000/graphql").httpEngine(DefaultHttpEngine(timeoutMillis = 60000)).build()
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun ApolloClient.fetchAppDataToDB(): Unit {
|
||||||
|
val kv = Database.kv
|
||||||
|
|
||||||
|
val req = this.query(FetchApplicationDataQuery("1125904232173609")).execute().data
|
||||||
|
|
||||||
|
req?.let {
|
||||||
|
for (task in req.tasks) {
|
||||||
|
kv.encode("task:${task.id}", task.toJson())
|
||||||
|
}
|
||||||
|
for (subtask in req.subtasks) {
|
||||||
|
kv.encode("subtask:${subtask.id}", subtask.toJson())
|
||||||
|
}
|
||||||
|
for (note in req.notes) {
|
||||||
|
kv.encode("note:${note.id}", note.toJson())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.example.mpdriver.components
|
package com.example.mpdriver.components
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
@ -17,6 +18,7 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Notifications
|
import androidx.compose.material.icons.outlined.Notifications
|
||||||
|
@ -39,13 +41,20 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.onFocusEvent
|
import androidx.compose.ui.focus.onFocusEvent
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.input.OffsetMapping
|
||||||
|
import androidx.compose.ui.text.input.TransformedText
|
||||||
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
@ -54,9 +63,17 @@ import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.apollographql.apollo3.api.ApolloResponse
|
||||||
|
import com.apollographql.apollo3.api.toInput
|
||||||
|
import com.example.mpdriver.GetSubtaskByIDQuery
|
||||||
|
import com.example.mpdriver.GetTaskByIdQuery
|
||||||
import com.example.mpdriver.R
|
import com.example.mpdriver.R
|
||||||
import com.example.mpdriver.api.Subtasks
|
import com.example.mpdriver.api.Subtasks
|
||||||
|
import com.example.mpdriver.api.apolloClient
|
||||||
import com.example.mpdriver.recievers.TimeTickReciever
|
import com.example.mpdriver.recievers.TimeTickReciever
|
||||||
|
import com.example.mpdriver.storage.CreateUpdateTaskData
|
||||||
|
import com.example.mpdriver.storage.Database
|
||||||
|
import com.example.mpdriver.type.StatusEnumQl
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -64,10 +81,13 @@ import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.DateTimeUnit
|
import kotlinx.datetime.DateTimeUnit
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.datetime.TimeZone
|
||||||
import kotlinx.datetime.UtcOffset
|
import kotlinx.datetime.UtcOffset
|
||||||
import kotlinx.datetime.asTimeZone
|
import kotlinx.datetime.asTimeZone
|
||||||
import kotlinx.datetime.format.byUnicodePattern
|
import kotlinx.datetime.format.byUnicodePattern
|
||||||
|
import kotlinx.datetime.format.char
|
||||||
import kotlinx.datetime.toInstant
|
import kotlinx.datetime.toInstant
|
||||||
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import kotlinx.datetime.until
|
import kotlinx.datetime.until
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
@ -76,7 +96,7 @@ import kotlin.math.abs
|
||||||
fun Subtask(
|
fun Subtask(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
children: @Composable () -> Unit = {},
|
children: @Composable () -> Unit = {},
|
||||||
subtask: Subtasks = Subtasks(1, startPln = "2024-06-01T00:00", endPln = "2024-06-10T00:00"),
|
subtaskID: Long = 0,
|
||||||
footerButton: @Composable () -> Unit = {}
|
footerButton: @Composable () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -91,13 +111,39 @@ fun Subtask(
|
||||||
var nowTime by remember {
|
var nowTime by remember {
|
||||||
mutableStateOf(Clock.System.now())
|
mutableStateOf(Clock.System.now())
|
||||||
}
|
}
|
||||||
|
var subtaskResponse by remember {
|
||||||
|
mutableStateOf<GetSubtaskByIDQuery.Subtask?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
|
||||||
|
val sbtDB = Database.subtasks.find { it.id == subtaskID.toString() }
|
||||||
|
|
||||||
|
sbtDB?.let {
|
||||||
|
subtaskResponse = GetSubtaskByIDQuery.Subtask(
|
||||||
|
sbtDB.id,
|
||||||
|
sbtDB.text,
|
||||||
|
startPln = sbtDB.startPln,
|
||||||
|
endPln = sbtDB.endPln,
|
||||||
|
status = sbtDB.status
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
// subtaskResponse = apolloClient.query(GetSubtaskByIDQuery("1125904232173609", subtaskID.toString())).execute()
|
||||||
|
}
|
||||||
|
|
||||||
TimeTickReciever.registerHandler {
|
TimeTickReciever.registerHandler {
|
||||||
nowTime = Clock.System.now()
|
nowTime = Clock.System.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
val startPln = LocalDateTime.parse(subtask.startPln!!)
|
var startPln: LocalDateTime = Clock.System.now().toLocalDateTime(TimeZone.UTC)
|
||||||
val endPln = LocalDateTime.parse(subtask.endPln!!)
|
var endPln: LocalDateTime = Clock.System.now().toLocalDateTime(TimeZone.UTC)
|
||||||
|
|
||||||
|
subtaskResponse?.let {
|
||||||
|
startPln = LocalDateTime.parse(subtaskResponse?.startPln.toString())
|
||||||
|
endPln = LocalDateTime.parse(subtaskResponse?.endPln.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val dateFormat = LocalDateTime.Format {
|
val dateFormat = LocalDateTime.Format {
|
||||||
byUnicodePattern("d.MM.yyyy")
|
byUnicodePattern("d.MM.yyyy")
|
||||||
|
@ -119,10 +165,10 @@ fun Subtask(
|
||||||
|
|
||||||
|
|
||||||
val status = when {
|
val status = when {
|
||||||
subtask.status == "Completed" && !isDelay -> TaskStatus.SUCCESS
|
subtaskResponse?.status == StatusEnumQl.COMPLETED && !isDelay -> TaskStatus.SUCCESS
|
||||||
subtask.status == "Completed" && isDelay -> TaskStatus.WARNING
|
subtaskResponse?.status == StatusEnumQl.COMPLETED && isDelay -> TaskStatus.WARNING
|
||||||
subtask.status == "Cancelled" -> TaskStatus.WARNING
|
subtaskResponse?.status == StatusEnumQl.CANCELLED -> TaskStatus.WARNING
|
||||||
subtask.status == "InProgress" || subtask.status == "NotDefined" && isDelay -> TaskStatus.DANGER
|
(subtaskResponse?.status == StatusEnumQl.IN_PROGRESS || subtaskResponse?.status == StatusEnumQl.NOT_DEFINED) && isDelay -> TaskStatus.DANGER
|
||||||
else -> TaskStatus.DEFAULT
|
else -> TaskStatus.DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +197,7 @@ fun Subtask(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
text = "${subtask.text}",
|
text = "${subtaskResponse?.text}",
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 20.sp
|
fontSize = 20.sp
|
||||||
)
|
)
|
||||||
|
@ -259,8 +305,8 @@ fun Subtask(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isActionVisible) {
|
if (isActionVisible && subtaskResponse != null) {
|
||||||
IsSubtaskCompletedAction(setStateAction = { isActionVisible = false }, subtask)
|
IsSubtaskCompletedAction(setStateAction = { isActionVisible = false }, subtaskResponse!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -268,7 +314,10 @@ fun Subtask(
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun IsSubtaskCompletedAction(setStateAction: (Boolean) -> Unit = { }, subtask: Subtasks) {
|
fun IsSubtaskCompletedAction(
|
||||||
|
setStateAction: (Boolean) -> Unit = { },
|
||||||
|
subtask: GetSubtaskByIDQuery.Subtask
|
||||||
|
) {
|
||||||
var currentStep by remember {
|
var currentStep by remember {
|
||||||
mutableStateOf(0)
|
mutableStateOf(0)
|
||||||
}
|
}
|
||||||
|
@ -318,6 +367,10 @@ fun IsSubtaskCompletedAction(setStateAction: (Boolean) -> Unit = { }, subtask: S
|
||||||
composable("success") {
|
composable("success") {
|
||||||
SuccessStep(subtask = subtask, controller = actionController)
|
SuccessStep(subtask = subtask, controller = actionController)
|
||||||
title = "Когда вы выполнили подзадачу?"
|
title = "Когда вы выполнили подзадачу?"
|
||||||
|
isFullScreen = true
|
||||||
|
LaunchedEffect(sheetState) {
|
||||||
|
sheetState.expand()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
composable("failure") {
|
composable("failure") {
|
||||||
isFullScreen = true
|
isFullScreen = true
|
||||||
|
@ -339,13 +392,13 @@ fun IsSubtaskCompletedAction(setStateAction: (Boolean) -> Unit = { }, subtask: S
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InitialStep(subtask: Subtasks, controller: NavHostController) {
|
fun InitialStep(subtask: GetSubtaskByIDQuery.Subtask, controller: NavHostController) {
|
||||||
Column {
|
Column {
|
||||||
JDEButton(type = ButtonType.SUCCESS, onClick = { controller.navigate("success") }) {
|
JDEButton(type = ButtonType.SUCCESS, onClick = { controller.navigate("success") }) {
|
||||||
Text(text = "Подзадача выполнена", fontSize = 15.sp, fontWeight = FontWeight.Bold)
|
Text(text = "Подзадача выполнена", fontSize = 15.sp, fontWeight = FontWeight.Bold)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
JDEButton(type = ButtonType.WARNING, onClick = {controller.navigate("failure")}) {
|
JDEButton(type = ButtonType.WARNING, onClick = { controller.navigate("failure") }) {
|
||||||
Text(text = "У меня возникла проблема", fontSize = 15.sp, fontWeight = FontWeight.Bold)
|
Text(text = "У меня возникла проблема", fontSize = 15.sp, fontWeight = FontWeight.Bold)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(40.dp))
|
Spacer(modifier = Modifier.height(40.dp))
|
||||||
|
@ -354,10 +407,9 @@ fun InitialStep(subtask: Subtasks, controller: NavHostController) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SuccessStep(
|
fun SuccessStep(
|
||||||
subtask: Subtasks = Subtasks(),
|
subtask: GetSubtaskByIDQuery.Subtask,
|
||||||
controller: NavHostController = rememberNavController()
|
controller: NavHostController = rememberNavController()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -402,26 +454,96 @@ fun SuccessStep(
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
Row(Modifier.fillMaxWidth()) {
|
Row(Modifier.fillMaxWidth()) {
|
||||||
TextInput(modifier = Modifier.weight(.66f), date, { date = it }, "Дата")
|
TextInput(modifier = Modifier.weight(.66f), date, {
|
||||||
|
date = it.filter { it.isDigit() }
|
||||||
|
}, "Дата")
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
TextInput(modifier = Modifier.weight(.33f), time, { date = time }, "Время")
|
TextInput(
|
||||||
|
modifier = Modifier.weight(.33f),
|
||||||
|
time,
|
||||||
|
{ time = it.filter { it.isDigit() } },
|
||||||
|
"Время"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
ActiveButton(onClick = { /*TODO*/ }, text = "Сохранить", modifier = Modifier.fillMaxWidth())
|
ActiveButton(onClick = {
|
||||||
|
val task = Database.tasks.find {
|
||||||
|
it.subtasks.find { sbt ->
|
||||||
|
sbt.id == subtask.id
|
||||||
|
} != null
|
||||||
|
}
|
||||||
|
|
||||||
|
val dt = LocalDateTime.parse(date + "T" + time, LocalDateTime.Format {
|
||||||
|
dayOfMonth()
|
||||||
|
monthNumber()
|
||||||
|
year()
|
||||||
|
char('T')
|
||||||
|
hour()
|
||||||
|
minute()
|
||||||
|
}).toInstant(TimeZone.UTC).toEpochMilliseconds()
|
||||||
|
|
||||||
|
task?.let {
|
||||||
|
Database.createUpdateTaskDataLocally(
|
||||||
|
CreateUpdateTaskData(
|
||||||
|
subtask.id.toLong(),
|
||||||
|
dt = dt,
|
||||||
|
status = "Completed"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (subtask.id == task.subtasks.last().id) {
|
||||||
|
Database.createUpdateTaskDataLocally(
|
||||||
|
CreateUpdateTaskData(
|
||||||
|
task.id.toLong(),
|
||||||
|
dt = dt,
|
||||||
|
status = "Completed"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return@let
|
||||||
|
}
|
||||||
|
val curr_sbt_idx = task.subtasks.indexOf(task.subtasks.find { it.id == subtask.id })
|
||||||
|
|
||||||
|
Database.createUpdateTaskDataLocally(
|
||||||
|
CreateUpdateTaskData(
|
||||||
|
task.subtasks[curr_sbt_idx + 1].id.toLong(),
|
||||||
|
dt,
|
||||||
|
status = "InProgress"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}, text = "Сохранить", modifier = Modifier.fillMaxWidth())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun transformText(it: String): String {
|
||||||
|
var digits = it.filter { it.isDigit() }
|
||||||
|
if (digits.length > 8) {
|
||||||
|
digits = digits.slice(0..<8)
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = when (digits.length) {
|
||||||
|
in 0..2 -> "${digits}"
|
||||||
|
in 3..4 -> "${digits.substring(0..<2)}.${digits.substring(2)}"
|
||||||
|
in 5..8 -> "${digits.substring(0..<2)}.${digits.substring(2..<4)}.${digits.substring(4)}"
|
||||||
|
else -> digits
|
||||||
|
}
|
||||||
|
println(data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Preview(showBackground = true)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FailureStep(
|
fun FailureStep(
|
||||||
subtask: Subtasks = Subtasks(),
|
subtask: GetSubtaskByIDQuery.Subtask,
|
||||||
controller: NavHostController = rememberNavController()
|
controller: NavHostController = rememberNavController()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -440,7 +562,8 @@ fun FailureStep(
|
||||||
color = Color.Gray
|
color = Color.Gray
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(40.dp))
|
Spacer(modifier = Modifier.height(40.dp))
|
||||||
TextField(value = failureDesk,
|
TextField(
|
||||||
|
value = failureDesk,
|
||||||
onValueChange = { failureDesk = it },
|
onValueChange = { failureDesk = it },
|
||||||
shape = RoundedCornerShape(10.dp),
|
shape = RoundedCornerShape(10.dp),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
@ -466,10 +589,12 @@ fun FailureStep(
|
||||||
focusedIndicatorColor = Color.Transparent,
|
focusedIndicatorColor = Color.Transparent,
|
||||||
unfocusedIndicatorColor = Color.Transparent
|
unfocusedIndicatorColor = Color.Transparent
|
||||||
),
|
),
|
||||||
trailingIcon = {Icon(
|
trailingIcon = {
|
||||||
painter = painterResource(id = R.drawable.calendar_default),
|
Icon(
|
||||||
contentDescription = ""
|
painter = painterResource(id = R.drawable.calendar_default),
|
||||||
)}
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
@ -485,15 +610,19 @@ fun FailureStep(
|
||||||
focusedIndicatorColor = Color.Transparent,
|
focusedIndicatorColor = Color.Transparent,
|
||||||
unfocusedIndicatorColor = Color.Transparent
|
unfocusedIndicatorColor = Color.Transparent
|
||||||
),
|
),
|
||||||
trailingIcon = {Icon(painter = painterResource(id = R.drawable.calendar_default),
|
trailingIcon = {
|
||||||
contentDescription = ""
|
Icon(
|
||||||
)}
|
painter = painterResource(id = R.drawable.calendar_default),
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
ActiveButton(onClick = { /*TODO*/ }, text = "Отправить", modifier = Modifier.fillMaxWidth())
|
ActiveButton(onClick = { /*TODO*/ }, text = "Отправить", modifier = Modifier.fillMaxWidth())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun TextInput(
|
fun TextInput(
|
||||||
|
@ -507,6 +636,8 @@ fun TextInput(
|
||||||
mutableStateOf(true)
|
mutableStateOf(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val kbController = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
|
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
@ -515,7 +646,13 @@ fun TextInput(
|
||||||
},
|
},
|
||||||
value = value,
|
value = value,
|
||||||
onValueChange = onValueChange,
|
onValueChange = onValueChange,
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Number,
|
||||||
|
imeAction = ImeAction.Done
|
||||||
|
),
|
||||||
|
keyboardActions = KeyboardActions(onDone = {
|
||||||
|
kbController?.hide()
|
||||||
|
}),
|
||||||
decorationBox = {
|
decorationBox = {
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
|
@ -525,16 +662,16 @@ fun TextInput(
|
||||||
.padding(horizontal = 10.dp, vertical = 5.dp)
|
.padding(horizontal = 10.dp, vertical = 5.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.align(if (isFocused) Alignment.TopStart else Alignment.CenterStart),
|
modifier = Modifier.align(if (isFocused || value != "") Alignment.TopStart else Alignment.CenterStart),
|
||||||
text = placeholder,
|
text = placeholder,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = if (isFocused) 13.sp else 18.sp,
|
fontSize = if (isFocused || value != "") 13.sp else 18.sp,
|
||||||
color = Color.Gray
|
color = Color.Gray
|
||||||
)
|
)
|
||||||
if (isFocused) {
|
if (isFocused || value != "") {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.align(Alignment.BottomStart),
|
modifier = Modifier.align(Alignment.BottomStart),
|
||||||
text = value,
|
text = transformText(value),
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,12 +14,14 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.KeyboardArrowUp
|
import androidx.compose.material.icons.rounded.KeyboardArrowUp
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
@ -32,10 +34,13 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
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 androidx.compose.ui.unit.sp
|
||||||
|
import com.example.mpdriver.GetTaskByIdQuery
|
||||||
import com.example.mpdriver.R
|
import com.example.mpdriver.R
|
||||||
import com.example.mpdriver.api.Subtasks
|
import com.example.mpdriver.api.apolloClient
|
||||||
import com.example.mpdriver.api.TaskResponse
|
|
||||||
import com.example.mpdriver.recievers.TimeTickReciever
|
import com.example.mpdriver.recievers.TimeTickReciever
|
||||||
|
import com.example.mpdriver.storage.Database
|
||||||
|
import com.example.mpdriver.type.MarshTemperaturePropertyQL
|
||||||
|
import com.example.mpdriver.type.StatusEnumQl
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.DateTimeUnit
|
import kotlinx.datetime.DateTimeUnit
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
@ -46,7 +51,6 @@ import kotlinx.datetime.toInstant
|
||||||
import kotlinx.datetime.until
|
import kotlinx.datetime.until
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
|
||||||
enum class TaskStatus {
|
enum class TaskStatus {
|
||||||
DANGER,
|
DANGER,
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
|
@ -58,8 +62,47 @@ enum class TaskStatus {
|
||||||
@Composable
|
@Composable
|
||||||
fun Task(modifier: Modifier = Modifier,
|
fun Task(modifier: Modifier = Modifier,
|
||||||
children: @Composable() ()->Unit = {},
|
children: @Composable() ()->Unit = {},
|
||||||
taskResponse: TaskResponse = TaskResponse(1, "2024-06-01T00:00", "2024-06-10T00:00"),
|
task_id: Long = 1,
|
||||||
footerButton: @Composable ()-> Unit = {}) {
|
footerButton: @Composable ()-> Unit = {}) {
|
||||||
|
var taskResponse by remember {
|
||||||
|
mutableStateOf<GetTaskByIdQuery.Data?>(null)
|
||||||
|
}
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
|
||||||
|
val dbTask = Database.tasks.find { it.id == task_id.toString() }
|
||||||
|
var task: GetTaskByIdQuery.Task
|
||||||
|
|
||||||
|
dbTask?.let {
|
||||||
|
|
||||||
|
val trailer = when(dbTask.route.trailer) {
|
||||||
|
null -> null
|
||||||
|
else -> GetTaskByIdQuery.Trailer(gost = dbTask.route.trailer.gost)
|
||||||
|
}
|
||||||
|
val truck = when(dbTask.route.truck) {
|
||||||
|
null -> null
|
||||||
|
else -> GetTaskByIdQuery.Truck(gost = dbTask.route.truck.gost)
|
||||||
|
}
|
||||||
|
|
||||||
|
val route = GetTaskByIdQuery.Route(name = dbTask.route.name, dbTask.route.temperatureProperty, truck, trailer)
|
||||||
|
task = GetTaskByIdQuery.Task(status = dbTask.status, route, text = dbTask.text, startPln = dbTask.startPln, endPln = dbTask.endPln, startFact = dbTask.startFact, endFact = dbTask.endFact)
|
||||||
|
taskResponse = GetTaskByIdQuery.Data(task)
|
||||||
|
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
taskResponse = apolloClient.query(GetTaskByIdQuery(task_id.toString(), "1125904232173609")).execute().data
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val dateFormat = LocalDateTime.Format {
|
||||||
|
byUnicodePattern("d.MM.yyyy")
|
||||||
|
}
|
||||||
|
|
||||||
|
val timeFormat = LocalDateTime.Format {
|
||||||
|
byUnicodePattern("HH:mm")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var expanded by remember {
|
var expanded by remember {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
@ -72,205 +115,208 @@ fun Task(modifier: Modifier = Modifier,
|
||||||
nowTime = Clock.System.now()
|
nowTime = Clock.System.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
val startPln = LocalDateTime.parse(taskResponse.startPln!!)
|
taskResponse?.let {
|
||||||
val endPln = LocalDateTime.parse(taskResponse.endPln!!)
|
val startPln = LocalDateTime.parse(it.task!!.startPln.toString())
|
||||||
|
val endPln = LocalDateTime.parse(it.task.endPln.toString())
|
||||||
|
val leastBase = nowTime.until(
|
||||||
|
endPln.toInstant(offset = UtcOffset(3)),
|
||||||
|
timeZone = UtcOffset(3).asTimeZone(), unit = DateTimeUnit.MINUTE)
|
||||||
|
|
||||||
val dateFormat = LocalDateTime.Format {
|
val isDelay = leastBase != abs(leastBase)
|
||||||
byUnicodePattern("d.MM.yyyy")
|
val leastDays = abs( leastBase) / 60 / 24
|
||||||
}
|
val leastHours = abs(leastBase) / 60 - leastDays * 24
|
||||||
|
val leastMinutes = abs(leastBase) - leastDays * 60 * 24 - leastHours * 60
|
||||||
val timeFormat = LocalDateTime.Format {
|
|
||||||
byUnicodePattern("HH:mm")
|
|
||||||
}
|
|
||||||
|
|
||||||
val leastBase = nowTime.until(
|
|
||||||
endPln.toInstant(offset = UtcOffset(3)),
|
|
||||||
timeZone = UtcOffset(3).asTimeZone(), unit = DateTimeUnit.MINUTE)
|
|
||||||
|
|
||||||
val isDelay = leastBase != abs(leastBase)
|
|
||||||
val leastDays = abs( leastBase) / 60 / 24
|
|
||||||
val leastHours = abs(leastBase) / 60 - leastDays * 24
|
|
||||||
val leastMinutes = abs(leastBase) - leastDays * 60 * 24 - leastHours * 60
|
|
||||||
|
|
||||||
|
|
||||||
val status = when {
|
val status = when {
|
||||||
taskResponse.status == "Completed" && !isDelay -> TaskStatus.SUCCESS
|
it.task.status == StatusEnumQl.COMPLETED && !isDelay -> TaskStatus.SUCCESS
|
||||||
taskResponse.status == "Completed" && isDelay -> TaskStatus.WARNING
|
it.task.status == StatusEnumQl.COMPLETED && isDelay -> TaskStatus.WARNING
|
||||||
taskResponse.status == "Cancelled" -> TaskStatus.WARNING
|
it.task.status == StatusEnumQl.CANCELLED -> TaskStatus.WARNING
|
||||||
taskResponse.status == "InProgress" || taskResponse.status == "NotDefined" && isDelay -> TaskStatus.DANGER
|
(it.task.status == StatusEnumQl.IN_PROGRESS || it.task.status == StatusEnumQl.NOT_DEFINED) && isDelay -> TaskStatus.DANGER
|
||||||
else -> TaskStatus.DEFAULT
|
else -> TaskStatus.DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Column(
|
||||||
Column(
|
modifier
|
||||||
modifier
|
.border(
|
||||||
.border(
|
2.dp, when (status) {
|
||||||
2.dp, when (status) {
|
TaskStatus.DEFAULT -> Color.Gray
|
||||||
TaskStatus.DEFAULT -> Color.Gray
|
TaskStatus.SUCCESS -> Color(0xFF45900B)
|
||||||
TaskStatus.SUCCESS -> Color(0xFF45900B)
|
TaskStatus.DANGER -> Color(0xFFE5332A)
|
||||||
TaskStatus.DANGER -> Color(0xFFE5332A)
|
TaskStatus.WARNING -> Color(0xFFFFC700)
|
||||||
TaskStatus.WARNING -> Color(0xFFFFC700)
|
}, RoundedCornerShape(10.dp)
|
||||||
}, RoundedCornerShape(10.dp)
|
|
||||||
)
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 15.dp, vertical = 15.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(bottom = 15.dp),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
text = "Движение по маршруту\n${taskResponse.route?.name}",
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = 20.sp
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
|
||||||
|
|
||||||
IconButton(onClick = { /*TODO*/ }, modifier = Modifier.weight(0.1f)) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(id = R.drawable.tick_default),
|
|
||||||
contentDescription = "tick"
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
Row(Modifier.padding(bottom = 20.dp)) {
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier.weight(1f),
|
|
||||||
mainText = "${taskResponse.route?.truck?.gost}",
|
|
||||||
subText = "Номер ТС"
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier.weight(1f),
|
|
||||||
mainText = "${if (taskResponse.route!!.trailer != null) taskResponse.route!!.trailer!!.gost else "-"}",
|
|
||||||
subText = "Номер прицепа"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 20.dp),
|
.padding(horizontal = 15.dp, vertical = 15.dp)
|
||||||
mainText = if (taskResponse.route!!.temperatureProperty == 1) "Горячая" else "Холодная",
|
|
||||||
subText = "Тип перевозки"
|
|
||||||
) {
|
) {
|
||||||
when (taskResponse.route!!.temperatureProperty) {
|
Row(
|
||||||
1 -> Image(painter = painterResource(id = R.drawable.hottransport), contentDescription = "" )
|
Modifier
|
||||||
else -> Image(painter = painterResource(id = R.drawable.coltransport), contentDescription = "" )
|
.fillMaxWidth()
|
||||||
}
|
.padding(bottom = 15.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
text = "Движение по маршруту\n${it.task.route.name}",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 20.sp
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
|
||||||
}
|
IconButton(onClick = { /*TODO*/ }, modifier = Modifier.weight(0.1f)) {
|
||||||
children()
|
Image(
|
||||||
AnimatedVisibility(visible = expanded) {
|
painter = painterResource(id = R.drawable.tick_default),
|
||||||
Column {
|
contentDescription = "tick"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(Modifier.padding(bottom = 20.dp)) {
|
||||||
InformationPlaceholderSmall(
|
InformationPlaceholderSmall(
|
||||||
Modifier
|
Modifier.weight(1f),
|
||||||
.fillMaxWidth()
|
mainText = "${if (it.task.route.truck != null) it.task.route.truck.gost else "-"}",
|
||||||
.padding(bottom = 20.dp),
|
subText = "Номер ТС"
|
||||||
mainText = "${taskResponse.text}",
|
|
||||||
subText = "Полный маршрут"
|
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
Text(
|
InformationPlaceholderSmall(
|
||||||
modifier = Modifier.padding(bottom = 5.dp),
|
Modifier.weight(1f),
|
||||||
text = "Начало маршрута",
|
mainText = "${if (it.task.route.trailer != null) it.task.route.trailer.gost else "-"}",
|
||||||
fontWeight = FontWeight.Bold,
|
subText = "Номер прицепа"
|
||||||
fontSize = 14.sp
|
|
||||||
)
|
)
|
||||||
Row(Modifier.padding(bottom = 20.dp)) {
|
}
|
||||||
InformationPlaceholderSmall(
|
InformationPlaceholderSmall(
|
||||||
Modifier.weight(2f),
|
Modifier
|
||||||
mainText = dateFormat.format(startPln),
|
.fillMaxWidth()
|
||||||
subText = "Дата"
|
.padding(bottom = 20.dp),
|
||||||
)
|
mainText = if (it.task.route.temperatureProperty == MarshTemperaturePropertyQL.HOT) "Горячая" else "Холодная",
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
subText = "Тип перевозки"
|
||||||
InformationPlaceholderSmall(
|
) {
|
||||||
Modifier.weight(1f),
|
when (it.task.route.temperatureProperty) {
|
||||||
mainText = timeFormat.format(startPln),
|
MarshTemperaturePropertyQL.HOT -> Image(painter = painterResource(id = R.drawable.hottransport), contentDescription = "" )
|
||||||
subText = "Время"
|
else -> Image(painter = painterResource(id = R.drawable.coltransport), contentDescription = "" )
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(bottom = 5.dp),
|
|
||||||
text = "Конец маршрута",
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = 14.sp
|
|
||||||
)
|
|
||||||
Row(Modifier.padding(bottom = 20.dp)) {
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier.weight(2f),
|
|
||||||
mainText = dateFormat.format(endPln),
|
|
||||||
subText = "Дата"
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier.weight(1f),
|
|
||||||
mainText = timeFormat.format(endPln),
|
|
||||||
subText = "Время"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(bottom = 5.dp),
|
|
||||||
text = if (isDelay) "Задержка" else "Осталось времени до конца маршрута",
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = if (isDelay) 20.sp else 14.sp,
|
|
||||||
color = if (isDelay) Color(0xFFE5332A) else Color.Black
|
|
||||||
)
|
|
||||||
Row(Modifier.padding(bottom = 20.dp)) {
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.border(
|
|
||||||
1.dp,
|
|
||||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
|
||||||
shape = RoundedCornerShape(10.dp)
|
|
||||||
),
|
|
||||||
mainText = leastDays.toString(),
|
|
||||||
subText = "Дни"
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.border(
|
|
||||||
1.dp,
|
|
||||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
|
||||||
shape = RoundedCornerShape(10.dp)
|
|
||||||
),
|
|
||||||
mainText = leastHours.toString(),
|
|
||||||
subText = "Часы"
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
|
||||||
InformationPlaceholderSmall(
|
|
||||||
Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.border(
|
|
||||||
1.dp,
|
|
||||||
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
|
||||||
shape = RoundedCornerShape(10.dp)
|
|
||||||
),
|
|
||||||
mainText = leastMinutes.toString(),
|
|
||||||
subText = "Минуты"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
children()
|
||||||
Divider(thickness = 2.dp, color = Color.Gray)
|
AnimatedVisibility(visible = expanded) {
|
||||||
TextButton(onClick = { expanded = !expanded }, colors = ButtonDefaults.buttonColors(contentColor = Color.Black, containerColor = Color.Transparent)) {
|
Column {
|
||||||
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
InformationPlaceholderSmall(
|
||||||
Text(text = if (expanded) "Скрыть" else "Развернуть", fontSize = 16.sp)
|
Modifier
|
||||||
Icon(Icons.Rounded.KeyboardArrowUp, contentDescription = "Chevron")
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 20.dp),
|
||||||
|
mainText = it.task.text,
|
||||||
|
subText = "Полный маршрут"
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(bottom = 5.dp),
|
||||||
|
text = "Начало маршрута",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
Row(Modifier.padding(bottom = 20.dp)) {
|
||||||
|
InformationPlaceholderSmall(
|
||||||
|
Modifier.weight(2f),
|
||||||
|
mainText = dateFormat.format(startPln),
|
||||||
|
subText = "Дата"
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
InformationPlaceholderSmall(
|
||||||
|
Modifier.weight(1f),
|
||||||
|
mainText = timeFormat.format(startPln),
|
||||||
|
subText = "Время"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(bottom = 5.dp),
|
||||||
|
text = "Конец маршрута",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
Row(Modifier.padding(bottom = 20.dp)) {
|
||||||
|
InformationPlaceholderSmall(
|
||||||
|
Modifier.weight(2f),
|
||||||
|
mainText = dateFormat.format(endPln),
|
||||||
|
subText = "Дата"
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
InformationPlaceholderSmall(
|
||||||
|
Modifier.weight(1f),
|
||||||
|
mainText = timeFormat.format(endPln),
|
||||||
|
subText = "Время"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(bottom = 5.dp),
|
||||||
|
text = if (isDelay) "Задержка" else "Осталось времени до конца маршрута",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = if (isDelay) 20.sp else 14.sp,
|
||||||
|
color = if (isDelay) Color(0xFFE5332A) else Color.Black
|
||||||
|
)
|
||||||
|
Row(Modifier.padding(bottom = 20.dp)) {
|
||||||
|
InformationPlaceholderSmall(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.border(
|
||||||
|
1.dp,
|
||||||
|
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||||
|
shape = RoundedCornerShape(10.dp)
|
||||||
|
),
|
||||||
|
mainText = leastDays.toString(),
|
||||||
|
subText = "Дни"
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
InformationPlaceholderSmall(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.border(
|
||||||
|
1.dp,
|
||||||
|
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||||
|
shape = RoundedCornerShape(10.dp)
|
||||||
|
),
|
||||||
|
mainText = leastHours.toString(),
|
||||||
|
subText = "Часы"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
InformationPlaceholderSmall(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.border(
|
||||||
|
1.dp,
|
||||||
|
color = if (isDelay) Color(0xFFE5332A) else Color.Gray,
|
||||||
|
shape = RoundedCornerShape(10.dp)
|
||||||
|
),
|
||||||
|
mainText = leastMinutes.toString(),
|
||||||
|
subText = "Минуты"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Divider(thickness = 2.dp, color = Color.Gray)
|
||||||
|
TextButton(onClick = { expanded = !expanded }, colors = ButtonDefaults.buttonColors(contentColor = Color.Black, containerColor = Color.Transparent)) {
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||||
|
Text(text = if (expanded) "Скрыть" else "Развернуть", fontSize = 16.sp)
|
||||||
|
Icon(Icons.Rounded.KeyboardArrowUp, contentDescription = "Chevron")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
footerButton()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
footerButton()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Column (
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
@ -39,24 +40,44 @@ 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 androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
import com.apollographql.apollo3.api.ApolloResponse
|
||||||
|
import com.example.mpdriver.GetActiveSubtaskIDQuery
|
||||||
|
import com.example.mpdriver.GetActiveTaskIdQuery
|
||||||
|
import com.example.mpdriver.GetTaskByIdQuery
|
||||||
import com.example.mpdriver.api.TaskResponse
|
import com.example.mpdriver.api.TaskResponse
|
||||||
|
import com.example.mpdriver.api.apolloClient
|
||||||
import com.example.mpdriver.components.ButtonType
|
import com.example.mpdriver.components.ButtonType
|
||||||
import com.example.mpdriver.components.EmptyList
|
import com.example.mpdriver.components.EmptyList
|
||||||
import com.example.mpdriver.components.IteractionButton
|
import com.example.mpdriver.components.IteractionButton
|
||||||
import com.example.mpdriver.components.JDEButton
|
import com.example.mpdriver.components.JDEButton
|
||||||
import com.example.mpdriver.components.Subtask
|
import com.example.mpdriver.components.Subtask
|
||||||
import com.example.mpdriver.components.Task
|
import com.example.mpdriver.components.Task
|
||||||
|
import com.example.mpdriver.storage.Database
|
||||||
|
import com.example.mpdriver.type.StatusEnumQl
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ActiveTask(activeTask: TaskResponse? = null, hostController: NavHostController) {
|
fun ActiveTask(activeTaskID: Long? = null, hostController: NavHostController) {
|
||||||
|
|
||||||
|
var responseGetActiveSubtaskID by remember {
|
||||||
|
mutableStateOf<GetActiveSubtaskIDQuery.ActiveSubtask?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTaskID?.let {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
val sbtDB = Database.subtasks.find { it.status == StatusEnumQl.IN_PROGRESS }
|
||||||
|
sbtDB?.let {
|
||||||
|
responseGetActiveSubtaskID = GetActiveSubtaskIDQuery.ActiveSubtask(sbtDB.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 15.dp)
|
.padding(vertical = 15.dp)
|
||||||
) {
|
) {
|
||||||
if (activeTask == null) {
|
if (activeTaskID == null) {
|
||||||
EmptyList(Modifier.padding(vertical = 60.dp), text = "У вас нет активных задач")
|
EmptyList(Modifier.padding(vertical = 60.dp), text = "У вас нет активных задач")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -66,9 +87,9 @@ fun ActiveTask(activeTask: TaskResponse? = null, hostController: NavHostControll
|
||||||
fontSize = 18.sp
|
fontSize = 18.sp
|
||||||
)
|
)
|
||||||
|
|
||||||
activeTask.let {
|
activeTaskID.let {
|
||||||
IteractionButton(onClick = { hostController.navigate("tasks/${it.id}") }) {
|
IteractionButton(onClick = { hostController.navigate("tasks/${activeTaskID}") }) {
|
||||||
Task(taskResponse = it)
|
Task(task_id = activeTaskID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,14 +102,14 @@ fun ActiveTask(activeTask: TaskResponse? = null, hostController: NavHostControll
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
|
||||||
activeTask.let { task ->
|
responseGetActiveSubtaskID.let { task ->
|
||||||
IteractionButton(onClick = { /*TODO*/ }) {
|
IteractionButton(onClick = { /*TODO*/ }) {
|
||||||
Subtask(subtask = task.subtasks.filter { it.status == "InProgress" }[0])
|
responseGetActiveSubtaskID?.let {
|
||||||
|
Subtask(subtaskID = it.id.toLong())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.example.mpdriver.components.note
|
||||||
|
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
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.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
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 com.example.mpdriver.components.ActiveButton
|
||||||
|
import com.example.mpdriver.components.JDEButton
|
||||||
|
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun NoteComponent(modifier: Modifier = Modifier) {
|
||||||
|
Column(
|
||||||
|
modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.border(2.dp, color = Color.Gray, shape = RoundedCornerShape(10.dp))
|
||||||
|
.padding(15.dp)) {
|
||||||
|
Text(text = "Изменился статус события", fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
||||||
|
Text(text = "Изменился статус события\n“Прибыть на ПГР 20.12.2023 к 10:00”", fontSize = 15.sp, fontWeight = FontWeight.Normal, color = Color.Gray)
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
ActiveButton(onClick = { /*TODO*/ }, text = "Перейти к задаче", modifier = Modifier.fillMaxWidth())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package com.example.mpdriver.screens
|
package com.example.mpdriver.screens
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
@ -9,6 +10,7 @@ import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
@ -19,11 +21,17 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
import com.apollographql.apollo3.api.ApolloResponse
|
||||||
|
import com.example.mpdriver.GetActiveTaskIdQuery
|
||||||
|
import com.example.mpdriver.GetTaskByIdQuery
|
||||||
import com.example.mpdriver.api.TaskApi
|
import com.example.mpdriver.api.TaskApi
|
||||||
import com.example.mpdriver.api.TaskResponse
|
import com.example.mpdriver.api.TaskResponse
|
||||||
|
import com.example.mpdriver.api.apolloClient
|
||||||
import com.example.mpdriver.components.Layout
|
import com.example.mpdriver.components.Layout
|
||||||
import com.example.mpdriver.components.feed.ActiveTask
|
import com.example.mpdriver.components.feed.ActiveTask
|
||||||
import com.example.mpdriver.components.feed.FeedTaskDataCard
|
import com.example.mpdriver.components.feed.FeedTaskDataCard
|
||||||
|
import com.example.mpdriver.storage.Database
|
||||||
|
import com.example.mpdriver.type.StatusEnumQl
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
import kotlinx.datetime.format.byUnicodePattern
|
import kotlinx.datetime.format.byUnicodePattern
|
||||||
|
|
||||||
|
@ -37,29 +45,17 @@ fun Feed(modifier: Modifier = Modifier, hostController: NavHostController) {
|
||||||
mutableStateOf(true)
|
mutableStateOf(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
var activeTask by remember {
|
var activeTask by remember {
|
||||||
mutableStateOf<TaskResponse?>(null)
|
mutableStateOf<GetActiveTaskIdQuery.Task?>(null)
|
||||||
}
|
}
|
||||||
var plannedTasks by remember {
|
var plannedTasks by remember {
|
||||||
mutableStateOf<List<TaskResponse>>(emptyList())
|
mutableStateOf(emptyList<GetActiveTaskIdQuery.PlannedTask>())
|
||||||
}
|
}
|
||||||
var completedTasks by remember {
|
var completedTasks by remember {
|
||||||
mutableStateOf<List<TaskResponse>>(emptyList())
|
mutableStateOf(emptyList<GetActiveTaskIdQuery.CompletedTask>())
|
||||||
}
|
}
|
||||||
|
|
||||||
val taskApi = TaskApi(context)
|
|
||||||
|
|
||||||
taskApi.getActiveTask { task ->
|
|
||||||
isLoading = false
|
|
||||||
task.id?.let {
|
|
||||||
activeTask = task
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
taskApi.getPlannedTasks { tasks ->
|
|
||||||
plannedTasks = tasks
|
|
||||||
}
|
|
||||||
val dateFormat = LocalDateTime.Format {
|
val dateFormat = LocalDateTime.Format {
|
||||||
byUnicodePattern("dd.MM.yyyy")
|
byUnicodePattern("dd.MM.yyyy")
|
||||||
}
|
}
|
||||||
|
@ -70,7 +66,7 @@ fun Feed(modifier: Modifier = Modifier, hostController: NavHostController) {
|
||||||
"count" to plannedTasks.count(),
|
"count" to plannedTasks.count(),
|
||||||
"date" to when (plannedTasks.count()) {
|
"date" to when (plannedTasks.count()) {
|
||||||
0 -> "-"
|
0 -> "-"
|
||||||
else -> dateFormat.format(LocalDateTime.parse(plannedTasks[0].startPln!!))
|
else -> dateFormat.format(LocalDateTime.parse(plannedTasks[0].startPln.toString()))
|
||||||
},
|
},
|
||||||
"buttonLabel" to "Смотреть запланированные задачи",
|
"buttonLabel" to "Смотреть запланированные задачи",
|
||||||
"dateDescription" to "Ближайшая",
|
"dateDescription" to "Ближайшая",
|
||||||
|
@ -80,23 +76,59 @@ fun Feed(modifier: Modifier = Modifier, hostController: NavHostController) {
|
||||||
"count" to completedTasks.count(),
|
"count" to completedTasks.count(),
|
||||||
"date" to when (completedTasks.count()) {
|
"date" to when (completedTasks.count()) {
|
||||||
0 -> "-"
|
0 -> "-"
|
||||||
else -> dateFormat.format(LocalDateTime.parse(completedTasks[0].startPln!!))
|
else -> dateFormat.format(LocalDateTime.parse(completedTasks[0].startPln.toString()))
|
||||||
},
|
},
|
||||||
"buttonLabel" to "Смотреть завершенные задачи",
|
"buttonLabel" to "Смотреть завершенные задачи",
|
||||||
"dateDescription" to "Последняя",
|
"dateDescription" to "Последняя",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
|
||||||
|
val tasks = Database.tasks
|
||||||
|
|
||||||
|
val activeTaskDB = tasks.find { it.status == StatusEnumQl.IN_PROGRESS }
|
||||||
|
val completedTasksDB = tasks.filter { it.status == StatusEnumQl.COMPLETED }
|
||||||
|
val plannedTasksDB = tasks.filter { it.status == StatusEnumQl.NOT_DEFINED }
|
||||||
|
|
||||||
|
activeTask = when (activeTaskDB) {
|
||||||
|
null -> null
|
||||||
|
else -> GetActiveTaskIdQuery.Task(id = activeTaskDB.id)
|
||||||
|
}
|
||||||
|
completedTasks = when (completedTasksDB.count()) {
|
||||||
|
0 -> emptyList()
|
||||||
|
else -> completedTasksDB.map {t ->
|
||||||
|
GetActiveTaskIdQuery.CompletedTask(t.id, t.startPln)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plannedTasks = when (plannedTasksDB.count()) {
|
||||||
|
0 -> emptyList()
|
||||||
|
else -> plannedTasksDB.map { t ->
|
||||||
|
GetActiveTaskIdQuery.PlannedTask(t.id, t.startPln)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d("FEED:PlannedTasks", "${plannedTasks}")
|
||||||
|
Log.d("FEED:ActiveTask", "${activeTask}")
|
||||||
|
Log.d("FEED:CompletedTask", "${completedTasks}")
|
||||||
|
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
if(isLoading) {
|
if(isLoading) {
|
||||||
Column (Modifier.fillMaxWidth().padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column (
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Layout(dataList = dataList, header = {
|
Layout(dataList = dataList, header = {
|
||||||
Column {
|
Column {
|
||||||
ActiveTask(activeTask = activeTask, hostController = hostController)
|
ActiveTask(activeTaskID = activeTask?.id?.toLong(), hostController = hostController)
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.example.mpdriver.screens
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import com.example.mpdriver.components.Layout
|
||||||
|
import com.example.mpdriver.components.note.NoteComponent
|
||||||
|
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun NoteScreen() {
|
||||||
|
|
||||||
|
|
||||||
|
Layout(dataList = (0..5).toMutableList()) {
|
||||||
|
NoteComponent()
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.FloatingActionButton
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -40,6 +41,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import com.example.mpdriver.FetchApplicationDataQuery
|
||||||
import com.example.mpdriver.api.Subtasks
|
import com.example.mpdriver.api.Subtasks
|
||||||
import com.example.mpdriver.api.TaskApi
|
import com.example.mpdriver.api.TaskApi
|
||||||
import com.example.mpdriver.components.ComposableLifecycle
|
import com.example.mpdriver.components.ComposableLifecycle
|
||||||
|
@ -47,6 +49,8 @@ import com.example.mpdriver.components.HeaderTabs
|
||||||
import com.example.mpdriver.components.HeaderTabsData
|
import com.example.mpdriver.components.HeaderTabsData
|
||||||
import com.example.mpdriver.components.Layout
|
import com.example.mpdriver.components.Layout
|
||||||
import com.example.mpdriver.components.Subtask
|
import com.example.mpdriver.components.Subtask
|
||||||
|
import com.example.mpdriver.storage.Database
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
|
@ -65,8 +69,9 @@ fun SubtaskScreen(taskId: Long = 0) {
|
||||||
|
|
||||||
|
|
||||||
var datalist by remember {
|
var datalist by remember {
|
||||||
mutableStateOf<List<Subtasks>>(mutableListOf())
|
mutableStateOf(emptyList<FetchApplicationDataQuery.Subtask1>())
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeTab by remember {
|
var activeTab by remember {
|
||||||
mutableStateOf(0)
|
mutableStateOf(0)
|
||||||
}
|
}
|
||||||
|
@ -74,25 +79,24 @@ fun SubtaskScreen(taskId: Long = 0) {
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
var offset = listState.firstVisibleItemScrollOffset + listState.firstVisibleItemIndex
|
var offset = listState.firstVisibleItemScrollOffset + listState.firstVisibleItemIndex
|
||||||
|
|
||||||
val tabsData = listOf<HeaderTabsData>(HeaderTabsData(0, "Подзадачи"), HeaderTabsData(1, "События"))
|
val tabsData =
|
||||||
|
listOf<HeaderTabsData>(HeaderTabsData(0, "Подзадачи"), HeaderTabsData(1, "События"))
|
||||||
|
|
||||||
ComposableLifecycle { _, event ->
|
LaunchedEffect(Unit) {
|
||||||
when (event) {
|
val task = Database.tasks.find { it.id == taskId.toString() }
|
||||||
Lifecycle.Event.ON_CREATE -> {
|
datalist = Database.subtasks.filter { sbt ->
|
||||||
api.getSubtasks(taskId) {
|
task!!.subtasks.find { it.id == sbt.id } != null
|
||||||
datalist = it
|
}.sortedBy { LocalDateTime.parse(it.startPln.toString()) }
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isLoading) {
|
|
||||||
Column (
|
if (isLoading) {
|
||||||
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -100,18 +104,26 @@ fun SubtaskScreen(taskId: Long = 0) {
|
||||||
|
|
||||||
Layout(state = listState, dataList = datalist, header = {
|
Layout(state = listState, dataList = datalist, header = {
|
||||||
HeaderTabs(modifier = Modifier.onGloballyPositioned { lc ->
|
HeaderTabs(modifier = Modifier.onGloballyPositioned { lc ->
|
||||||
val cords = lc.boundsInParent()
|
val cords = lc.boundsInParent()
|
||||||
floatingActionShowOffset = cords.bottom
|
floatingActionShowOffset = cords.bottom
|
||||||
}, tabsData = tabsData, activeTab = activeTab) {
|
}, tabsData = tabsData, activeTab = activeTab) {
|
||||||
activeTab = it
|
activeTab = it
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Subtask(subtask = it)
|
Subtask(subtaskID = it.id!!.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(visible = offset + 200 > floatingActionShowOffset + 100, enter = slideInVertically() + fadeIn(), exit = slideOutVertically() + fadeOut()) {
|
AnimatedVisibility(
|
||||||
ExtendedFloatingActionButton(onClick = { /*TODO*/ },
|
visible = offset + 200 > floatingActionShowOffset + 100,
|
||||||
shape = RoundedCornerShape(10.dp), containerColor = Color.White, contentColor = Color.Black) {
|
enter = slideInVertically() + fadeIn(),
|
||||||
|
exit = slideOutVertically() + fadeOut()
|
||||||
|
) {
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
onClick = { /*TODO*/ },
|
||||||
|
shape = RoundedCornerShape(10.dp),
|
||||||
|
containerColor = Color.White,
|
||||||
|
contentColor = Color.Black
|
||||||
|
) {
|
||||||
Row(Modifier.fillMaxWidth()) {
|
Row(Modifier.fillMaxWidth()) {
|
||||||
Text(text = "Скрыть завершенные задачи")
|
Text(text = "Скрыть завершенные задачи")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -28,17 +29,25 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
|
import com.apollographql.apollo3.api.ApolloResponse
|
||||||
|
import com.example.mpdriver.GetPlannedTasksIDsQuery
|
||||||
|
import com.example.mpdriver.GetTaskByIdQuery
|
||||||
import com.example.mpdriver.api.TaskApi
|
import com.example.mpdriver.api.TaskApi
|
||||||
import com.example.mpdriver.api.TaskResponse
|
import com.example.mpdriver.api.TaskResponse
|
||||||
|
import com.example.mpdriver.api.apolloClient
|
||||||
import com.example.mpdriver.components.ActiveButton
|
import com.example.mpdriver.components.ActiveButton
|
||||||
import com.example.mpdriver.components.HeaderTabs
|
import com.example.mpdriver.components.HeaderTabs
|
||||||
import com.example.mpdriver.components.HeaderTabsData
|
import com.example.mpdriver.components.HeaderTabsData
|
||||||
import com.example.mpdriver.components.Layout
|
import com.example.mpdriver.components.Layout
|
||||||
import com.example.mpdriver.components.StaleButton
|
import com.example.mpdriver.components.StaleButton
|
||||||
import com.example.mpdriver.components.Task
|
import com.example.mpdriver.components.Task
|
||||||
|
import com.example.mpdriver.storage.CreateUpdateTaskData
|
||||||
import com.example.mpdriver.storage.Database
|
import com.example.mpdriver.storage.Database
|
||||||
|
import com.example.mpdriver.type.StatusEnumQl
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.datetime.Clock
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -50,26 +59,41 @@ fun TasksList(modifier: Modifier = Modifier, hostController: NavHostController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var itemsList by remember {
|
var itemsListResponse by remember {
|
||||||
mutableStateOf<List<TaskResponse>>(emptyList())
|
mutableStateOf(emptyList<GetPlannedTasksIDsQuery.Task>())
|
||||||
}
|
|
||||||
api.getPlannedTasks {
|
|
||||||
itemsList = it
|
|
||||||
isLoading = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var activeTab by remember {
|
var activeTab by remember {
|
||||||
mutableIntStateOf(0)
|
mutableIntStateOf(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
val d = Database.tasks.filter { it -> it.status == StatusEnumQl.NOT_DEFINED}
|
||||||
|
|
||||||
|
itemsListResponse = when(d.count()) {
|
||||||
|
0 -> emptyList()
|
||||||
|
else -> d.map {
|
||||||
|
val sbts = it.subtasks.map {sbt ->
|
||||||
|
GetPlannedTasksIDsQuery.Subtask(sbt.id)
|
||||||
|
}
|
||||||
|
GetPlannedTasksIDsQuery.Task(id = it.id, sbts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(isLoading) {
|
if(isLoading) {
|
||||||
Column (Modifier.fillMaxWidth().padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
Column (
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 60.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
CircularProgressIndicator(color = Color(0xFFE5332A))
|
CircularProgressIndicator(color = Color(0xFFE5332A))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Layout(dataList = itemsList, header = {
|
Layout(dataList = itemsListResponse, header = {
|
||||||
HeaderTabs(
|
HeaderTabs(
|
||||||
tabsData = listOf(
|
tabsData = listOf(
|
||||||
HeaderTabsData(0, "Запланированные"),
|
HeaderTabsData(0, "Запланированные"),
|
||||||
|
@ -79,15 +103,24 @@ fun TasksList(modifier: Modifier = Modifier, hostController: NavHostController)
|
||||||
activeTab = it
|
activeTab = it
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Task(Modifier.padding(bottom = 10.dp), taskResponse = it) {
|
Task(Modifier.padding(bottom = 10.dp), task_id = it.id.toLong()) {
|
||||||
when (activeTab) {
|
when (activeTab) {
|
||||||
0 -> Button(
|
0 -> Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
api.setTaskStatusInProgress(it.id!!) {
|
Database.createUpdateTaskDataLocally(
|
||||||
MainScope().launch {
|
CreateUpdateTaskData(
|
||||||
hostController.navigate("feed")
|
it.id.toLong(),
|
||||||
}
|
Clock.System.now().toEpochMilliseconds(),
|
||||||
}
|
"InProgress"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Database.createUpdateTaskDataLocally(
|
||||||
|
CreateUpdateTaskData(
|
||||||
|
it.subtasks[0].id.toLong(), Clock.System.now().toEpochMilliseconds(), "InProgress",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hostController.navigate("feed")
|
||||||
|
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
contentColor = Color.White,
|
contentColor = Color.White,
|
||||||
|
@ -96,7 +129,7 @@ fun TasksList(modifier: Modifier = Modifier, hostController: NavHostController)
|
||||||
disabledContainerColor = Color.Gray
|
disabledContainerColor = Color.Gray
|
||||||
),
|
),
|
||||||
shape = RoundedCornerShape(10.dp),
|
shape = RoundedCornerShape(10.dp),
|
||||||
enabled = if (Database.tasks.filter { it.status == "InProgress" }.count() >= 1) false else true,
|
// enabled = if (Database.tasks.filter { it.status == "InProgress" }.count() >= 1) false else true,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(text = "Приступить к выполнению")
|
Text(text = "Приступить к выполнению")
|
||||||
|
|
|
@ -1,43 +1,28 @@
|
||||||
package com.example.mpdriver.storage
|
package com.example.mpdriver.storage
|
||||||
|
|
||||||
import com.example.mpdriver.api.TaskResponse
|
import android.util.Log
|
||||||
|
import com.benasher44.uuid.uuid4
|
||||||
|
import com.example.mpdriver.FetchApplicationDataQuery
|
||||||
|
import com.example.mpdriver.api.toJson
|
||||||
|
import com.example.mpdriver.type.DateTime
|
||||||
|
import com.example.mpdriver.type.StatusEnumQl
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import java.lang.reflect.Type
|
|
||||||
|
|
||||||
|
data class CreateUpdateTaskData(
|
||||||
|
val task_id: Long,
|
||||||
|
val dt: Long,
|
||||||
|
val status: String,
|
||||||
|
val error_text: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
object Database {
|
object Database {
|
||||||
|
|
||||||
val kv = MMKV.defaultMMKV()
|
val kv = MMKV.defaultMMKV()
|
||||||
|
|
||||||
var tasks: List<TaskResponse>
|
val allKeys: Array<out String>?
|
||||||
get() {
|
get() = kv.allKeys()
|
||||||
val data = kv.decodeString("tasks")
|
|
||||||
data?.let { json ->
|
|
||||||
val type: Type = object : TypeToken<List<TaskResponse>>() {}.type
|
|
||||||
return Gson().fromJson(json, type)
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
val data = Gson().toJson(value)
|
|
||||||
kv.encode("tasks", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
var planned_tasks: List<TaskResponse>
|
|
||||||
get() {
|
|
||||||
val data = kv.decodeString("planned_tasks")
|
|
||||||
data?.let { json ->
|
|
||||||
val type: Type = object : TypeToken<List<TaskResponse>>() {}.type
|
|
||||||
return Gson().fromJson(json, type)
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
val data = Gson().toJson(value)
|
|
||||||
println(value)
|
|
||||||
println(data)
|
|
||||||
kv.encode("planned_tasks", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
var access_token: String?
|
var access_token: String?
|
||||||
get() {
|
get() {
|
||||||
|
@ -52,4 +37,96 @@ object Database {
|
||||||
set(value) {
|
set(value) {
|
||||||
kv.encode("phone_number", value)
|
kv.encode("phone_number", value)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private inline fun <reified D> parseData(prefix: String): MutableList<D> {
|
||||||
|
val returnedList = mutableListOf<D>()
|
||||||
|
|
||||||
|
for (key in allKeys!!) {
|
||||||
|
if (key.startsWith(prefix)) {
|
||||||
|
val data = kv.decodeString(key)
|
||||||
|
data.let {
|
||||||
|
returnedList.add(Gson().fromJson(it, D::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnedList
|
||||||
|
}
|
||||||
|
|
||||||
|
val tasks: MutableList<FetchApplicationDataQuery.Task>
|
||||||
|
get() = parseData("task:")
|
||||||
|
|
||||||
|
val notes: MutableList<FetchApplicationDataQuery.Note>
|
||||||
|
get() = parseData("note:")
|
||||||
|
|
||||||
|
val subtasks: MutableList<FetchApplicationDataQuery.Subtask1>
|
||||||
|
get() = parseData("subtask:")
|
||||||
|
|
||||||
|
|
||||||
|
val updateTasks: MutableList<CreateUpdateTaskData>
|
||||||
|
get() = parseData("update:task:")
|
||||||
|
|
||||||
|
fun createUpdateTaskDataLocally(d: CreateUpdateTaskData) {
|
||||||
|
|
||||||
|
kv.encode("update:task:${uuid4()}", d.toJson())
|
||||||
|
val task = tasks.find { it.id == d.task_id.toString() }
|
||||||
|
val sbt = subtasks.find { it.id == d.task_id.toString() }
|
||||||
|
|
||||||
|
task?.let {
|
||||||
|
|
||||||
|
val newTask = FetchApplicationDataQuery.Task(
|
||||||
|
id = task.id,
|
||||||
|
startPln = task.startPln,
|
||||||
|
startFact = task.startFact,
|
||||||
|
endPln = task.endPln,
|
||||||
|
endFact = task.endFact,
|
||||||
|
status = when (d.status) {
|
||||||
|
"InProgress" -> StatusEnumQl.IN_PROGRESS
|
||||||
|
"Completed" -> StatusEnumQl.COMPLETED
|
||||||
|
"Cancelled" -> StatusEnumQl.CANCELLED
|
||||||
|
else -> task.status
|
||||||
|
},
|
||||||
|
events = task.events,
|
||||||
|
subtasks = task.subtasks,
|
||||||
|
taskType = task.taskType,
|
||||||
|
route = task.route,
|
||||||
|
text = task.text
|
||||||
|
)
|
||||||
|
kv.encode("task:${newTask.id}", newTask.toJson())
|
||||||
|
// Log.d("MMKV: task:${newTask.id}", kv.decodeString("task:${newTask.id}")!!)
|
||||||
|
Log.d("MMKV.KEYS", allKeys!!.toMutableList().joinToString(", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
sbt?.let { s ->
|
||||||
|
val newSubtask = FetchApplicationDataQuery.Subtask1(
|
||||||
|
id = s.id,
|
||||||
|
startPln = s.startPln,
|
||||||
|
startFact = s.startFact,
|
||||||
|
endPln = s.endPln,
|
||||||
|
endFact = s.endFact,
|
||||||
|
station = s.station,
|
||||||
|
status = when (d.status) {
|
||||||
|
"InProgress" -> StatusEnumQl.IN_PROGRESS
|
||||||
|
"Completed" -> StatusEnumQl.COMPLETED
|
||||||
|
"Cancelled" -> StatusEnumQl.CANCELLED
|
||||||
|
else -> s.status
|
||||||
|
},
|
||||||
|
taskType = s.taskType,
|
||||||
|
text = s.text
|
||||||
|
)
|
||||||
|
kv.encode("subtask:${newSubtask.id}", newSubtask.toJson())
|
||||||
|
Log.d("MMKV.KEYS", allKeys!!.toMutableList().joinToString(", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun dropUpdates() {
|
||||||
|
for (key in allKeys!!){
|
||||||
|
if (key.startsWith("update:task:")) {
|
||||||
|
kv.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||||
|
id("com.google.gms.google-services") version "4.4.2" apply false
|
||||||
}
|
}
|
Loading…
Reference in New Issue