├── .fleet └── receipt.json ├── .gitignore ├── README.md ├── build.gradle.kts ├── composeApp ├── build.gradle.kts ├── jetSpring.log └── src │ ├── commonMain │ ├── composeResources │ │ └── drawable │ │ │ └── compose-multiplatform.xml │ ├── kotlin │ │ └── ixidev │ │ │ └── jetspring │ │ │ ├── base │ │ │ └── BaseViewModel.kt │ │ │ ├── data │ │ │ ├── DataGenerator.kt │ │ │ ├── DataService.kt │ │ │ ├── entities │ │ │ │ ├── AbstractEntity.kt │ │ │ │ ├── BaseEntity.kt │ │ │ │ ├── Role.kt │ │ │ │ └── User.kt │ │ │ └── services │ │ │ │ ├── UserRepository.kt │ │ │ │ └── UserService.kt │ │ │ ├── security │ │ │ ├── AppSecurityConfig.kt │ │ │ ├── AuthenticatedUser.kt │ │ │ ├── UserAuthSate.kt │ │ │ └── UserDetailsServiceImpl.kt │ │ │ ├── ui │ │ │ ├── GlobalViewModel.kt │ │ │ ├── auth │ │ │ │ ├── LoginScreen.kt │ │ │ │ └── LoginViewModel.kt │ │ │ ├── components │ │ │ │ ├── AppMenuHeader.kt │ │ │ │ ├── AppWindowTitleB.kt │ │ │ │ ├── NavigationMenuItem.kt │ │ │ │ ├── SideBarMenu.kt │ │ │ │ ├── TextComponenets.kt │ │ │ │ └── ValidatorTextField.kt │ │ │ ├── home │ │ │ │ ├── HomeScreen.kt │ │ │ │ └── HomeViewModel.kt │ │ │ ├── navigation │ │ │ │ ├── Screens.kt │ │ │ │ └── navcontroller │ │ │ │ │ ├── NavController.kt │ │ │ │ │ └── NavigationHost.kt │ │ │ └── theme │ │ │ │ ├── AppThemeState.kt │ │ │ │ ├── Color.kt │ │ │ │ ├── Shape.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── utils │ │ │ └── ExtHelpers.kt │ └── resources │ │ ├── Almarai-Regular.ttf │ │ ├── app-icon.svg │ │ ├── application.properties │ │ ├── strings.properties │ │ └── strings_ar.properties │ └── desktopMain │ └── kotlin │ └── ixidev │ └── jetspring │ └── Main.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.fleet/receipt.json: -------------------------------------------------------------------------------- 1 | // Project generated by Kotlin Multiplatform Wizard 2 | { 3 | "spec": { 4 | "template_id": "kmt", 5 | "targets": { 6 | "desktop": { 7 | "ui": [ 8 | "compose" 9 | ] 10 | } 11 | } 12 | }, 13 | "timestamp": "2024-05-30T21:07:06.315659137Z" 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .kotlin 3 | .gradle 4 | **/build/ 5 | xcuserdata 6 | !src/**/build/ 7 | local.properties 8 | .idea 9 | .DS_Store 10 | captures 11 | .externalNativeBuild 12 | .cxx 13 | *.xcodeproj/* 14 | !*.xcodeproj/project.pbxproj 15 | !*.xcodeproj/xcshareddata/ 16 | !*.xcodeproj/project.xcworkspace/ 17 | !*.xcworkspace/contents.xcworkspacedata 18 | **/xcshareddata/WorkspaceSettings.xcsettings 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### JetSpring 2 | 3 | Fullstack Desktop app using : 4 | 5 | - Jetbrains Compose 🚀 6 | - Spring boot & MySQL & Hibernate 🤠 7 | - Kotlin ❤ 8 | 9 | ##### Requirements 10 | 11 | - For packaging the app as .deb or .exe file you need Java JDK 15+ 12 | - Install MySQL server and create a database named *jet_spring* take a look 13 | at [application.properties](composeApp/src/commonMain/resources/application.properties) to config database 14 | 15 | ### Screenshots 16 | 17 | #### Login Screen 18 | 19 | ###### Default authentication info 20 | 21 | - Username : *admin@jetspring.com* 22 | - Password : *admin* 23 | - Config default info in [DataGenerator.kt](composeApp/src/commonMain/kotlin/ixidev/jetspring/data/DataGenerator.kt) 24 | ![](screenshots/login_screen.png) 25 | 26 | #### Home Screen 27 | 28 | ![](screenshots/home_screen.png) 29 | 30 | #### Home Screen Dark 31 | 32 | ![](screenshots/home_screen_dark.png) 33 | 34 | 35 |
36 | 37 | ### Show some ❤ by stare the repo 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | // this is necessary to avoid the plugins to be loaded multiple times 3 | // in each subproject's classloader 4 | alias(libs.plugins.jetbrainsCompose) apply false 5 | alias(libs.plugins.compose.compiler) apply false 6 | alias(libs.plugins.kotlinMultiplatform) apply false 7 | } -------------------------------------------------------------------------------- /composeApp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat 2 | 3 | plugins { 4 | alias(libs.plugins.kotlinMultiplatform) 5 | alias(libs.plugins.jetbrainsCompose) 6 | alias(libs.plugins.compose.compiler) 7 | id("org.springframework.boot") version "3.3.0" 8 | id("io.spring.dependency-management") version "1.1.5" 9 | kotlin("plugin.spring") version "2.0.0" 10 | kotlin("plugin.jpa") version "2.0.0" 11 | } 12 | 13 | kotlin { 14 | jvm("desktop"){ 15 | withJava() 16 | } 17 | 18 | sourceSets { 19 | val desktopMain by getting 20 | 21 | commonMain.dependencies { 22 | implementation(compose.runtime) 23 | implementation(compose.foundation) 24 | implementation(compose.material) 25 | implementation(compose.ui) 26 | implementation(compose.components.resources) 27 | implementation(compose.components.uiToolingPreview) 28 | implementation("org.jetbrains.kotlin:kotlin-reflect") 29 | // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 30 | implementation("org.springframework.boot:spring-boot-starter-data-jpa") 31 | implementation("org.springframework.boot:spring-boot-starter-security") 32 | implementation("mysql:mysql-connector-java:8.0.33") 33 | // runtimeOnly("mysql:mysql-connector-java") 34 | implementation("br.com.devsrsouza.compose.icons.jetbrains:font-awesome:1.0.0") 35 | implementation("com.github.lgooddatepicker:LGoodDatePicker:11.2.1") 36 | compileOnly("org.projectlombok:lombok") 37 | } 38 | desktopMain.dependencies { 39 | implementation(compose.desktop.currentOs) 40 | } 41 | } 42 | } 43 | 44 | 45 | compose.desktop { 46 | application { 47 | mainClass = "ixidev.jetspring.MainKt" 48 | 49 | nativeDistributions { 50 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) 51 | packageName = "ixidev.jetspring.jtspring" 52 | packageVersion = "1.0.0" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /composeApp/jetSpring.log: -------------------------------------------------------------------------------- 1 | 2024-05-30T22:58:50.704+01:00 INFO 32519 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Starting application using Java 17.0.11 with PID 32519 (started by idali in /Users/idali/IdeaProjects/JtSpring2/composeApp) 2 | 2024-05-30T22:58:50.705+01:00 INFO 32519 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : No active profile set, falling back to 1 default profile: "default" 3 | 2024-05-30T22:58:50.866+01:00 INFO 32519 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 4 | 2024-05-30T22:58:50.883+01:00 INFO 32519 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 1 JPA repository interface. 5 | 2024-05-30T22:58:51.083+01:00 INFO 32519 --- [AWT-EventQueue-0] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 6 | 2024-05-30T22:58:51.103+01:00 INFO 32519 --- [AWT-EventQueue-0] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final 7 | 2024-05-30T22:58:51.115+01:00 INFO 32519 --- [AWT-EventQueue-0] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 8 | 2024-05-30T22:58:51.223+01:00 INFO 32519 --- [AWT-EventQueue-0] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 9 | 2024-05-30T22:58:51.232+01:00 INFO 32519 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 10 | 2024-05-30T22:58:52.286+01:00 WARN 32519 --- [AWT-EventQueue-0] o.h.e.j.e.i.JdbcEnvironmentInitiator : HHH000342: Could not obtain connection to query metadata 11 | 12 | java.lang.NullPointerException: Cannot invoke "org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(java.sql.SQLException, String)" because the return value of "org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.sqlExceptionHelper()" is null 13 | at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:116) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 14 | at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:290) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 15 | at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:123) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 16 | at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:77) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 17 | at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 18 | at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 19 | at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 20 | at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 21 | at org.hibernate.boot.model.relational.Database.(Database.java:45) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 22 | at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:221) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 23 | at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:189) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 24 | at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:171) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 25 | at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1431) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 26 | at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1502) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 27 | at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.8.jar:6.1.8] 28 | at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) ~[spring-orm-6.1.8.jar:6.1.8] 29 | at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.8.jar:6.1.8] 30 | at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.8.jar:6.1.8] 31 | at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) ~[spring-orm-6.1.8.jar:6.1.8] 32 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) ~[spring-beans-6.1.8.jar:6.1.8] 33 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) ~[spring-beans-6.1.8.jar:6.1.8] 34 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.8.jar:6.1.8] 35 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8] 36 | at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8] 37 | at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8] 38 | at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8] 39 | at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) ~[spring-beans-6.1.8.jar:6.1.8] 40 | at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) ~[spring-context-6.1.8.jar:6.1.8] 41 | at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar:6.1.8] 42 | at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.0.jar:3.3.0] 43 | at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.0.jar:3.3.0] 44 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.0.jar:3.3.0] 45 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.0.jar:3.3.0] 46 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.0.jar:3.3.0] 47 | at ixidev.jetspring.MainKt$main$1.invoke(Main.kt:192) ~[composeApp-desktop.jar:na] 48 | at ixidev.jetspring.MainKt$main$1.invoke(Main.kt:55) ~[composeApp-desktop.jar:na] 49 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:116) ~[runtime-desktop-1.6.10.jar:na] 50 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 51 | at androidx.compose.ui.window.Application_desktopKt$application$1$1.invoke(Application.desktop.kt:117) ~[ui-desktop-1.6.10.jar:na] 52 | at androidx.compose.ui.window.Application_desktopKt$application$1$1.invoke(Application.desktop.kt:116) ~[ui-desktop-1.6.10.jar:na] 53 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:116) ~[runtime-desktop-1.6.10.jar:na] 54 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 55 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1$1.invoke(Application.desktop.kt:233) ~[ui-desktop-1.6.10.jar:na] 56 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1$1.invoke(Application.desktop.kt:232) ~[ui-desktop-1.6.10.jar:na] 57 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:107) ~[runtime-desktop-1.6.10.jar:na] 58 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 59 | at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) ~[runtime-desktop-1.6.10.jar:na] 60 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1.invoke(Application.desktop.kt:223) ~[ui-desktop-1.6.10.jar:na] 61 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1.invoke(Application.desktop.kt:221) ~[ui-desktop-1.6.10.jar:na] 62 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:107) ~[runtime-desktop-1.6.10.jar:na] 63 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 64 | at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:33) ~[runtime-desktop-1.6.10.jar:na] 65 | at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3303) ~[runtime-desktop-1.6.10.jar:na] 66 | at androidx.compose.runtime.ComposerImpl.composeContent$runtime(Composer.kt:3236) ~[runtime-desktop-1.6.10.jar:na] 67 | at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:725) ~[runtime-desktop-1.6.10.jar:na] 68 | at androidx.compose.runtime.Recomposer.composeInitial$runtime(Recomposer.kt:1071) ~[runtime-desktop-1.6.10.jar:na] 69 | at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:633) ~[runtime-desktop-1.6.10.jar:na] 70 | at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:619) ~[runtime-desktop-1.6.10.jar:na] 71 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2.invokeSuspend(Application.desktop.kt:221) ~[ui-desktop-1.6.10.jar:na] 72 | at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-2.0.0.jar:2.0.0-release-341] 73 | at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) ~[kotlinx-coroutines-core-jvm-1.8.1.jar:na] 74 | at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318) ~[na:na] 75 | at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773) ~[na:na] 76 | at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720) ~[na:na] 77 | at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714) ~[na:na] 78 | at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) ~[na:na] 79 | at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) ~[na:na] 80 | at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742) ~[na:na] 81 | at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) ~[na:na] 82 | at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) ~[na:na] 83 | at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) ~[na:na] 84 | at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) ~[na:na] 85 | at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) ~[na:na] 86 | at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) ~[na:na] 87 | 88 | 2024-05-30T22:58:52.296+01:00 ERROR 32519 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA EntityManagerFactory: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persistence.jdbc.url' for common cases or 'hibernate.dialect' when a custom Dialect implementation must be provided) 89 | 2024-05-30T22:58:52.297+01:00 WARN 32519 --- [AWT-EventQueue-0] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persistence.jdbc.url' for common cases or 'hibernate.dialect' when a custom Dialect implementation must be provided) 90 | 2024-05-30T22:58:52.308+01:00 INFO 32519 --- [AWT-EventQueue-0] .s.b.a.l.ConditionEvaluationReportLogger : 91 | 92 | Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 93 | 2024-05-30T22:58:52.320+01:00 ERROR 32519 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Application run failed 94 | 95 | org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persistence.jdbc.url' for common cases or 'hibernate.dialect' when a custom Dialect implementation must be provided) 96 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788) ~[spring-beans-6.1.8.jar:6.1.8] 97 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.8.jar:6.1.8] 98 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8] 99 | at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8] 100 | at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8] 101 | at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8] 102 | at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) ~[spring-beans-6.1.8.jar:6.1.8] 103 | at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) ~[spring-context-6.1.8.jar:6.1.8] 104 | at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar:6.1.8] 105 | at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.0.jar:3.3.0] 106 | at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.0.jar:3.3.0] 107 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.0.jar:3.3.0] 108 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.0.jar:3.3.0] 109 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.0.jar:3.3.0] 110 | at ixidev.jetspring.MainKt$main$1.invoke(Main.kt:192) ~[composeApp-desktop.jar:na] 111 | at ixidev.jetspring.MainKt$main$1.invoke(Main.kt:55) ~[composeApp-desktop.jar:na] 112 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:116) ~[runtime-desktop-1.6.10.jar:na] 113 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 114 | at androidx.compose.ui.window.Application_desktopKt$application$1$1.invoke(Application.desktop.kt:117) ~[ui-desktop-1.6.10.jar:na] 115 | at androidx.compose.ui.window.Application_desktopKt$application$1$1.invoke(Application.desktop.kt:116) ~[ui-desktop-1.6.10.jar:na] 116 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:116) ~[runtime-desktop-1.6.10.jar:na] 117 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 118 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1$1.invoke(Application.desktop.kt:233) ~[ui-desktop-1.6.10.jar:na] 119 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1$1.invoke(Application.desktop.kt:232) ~[ui-desktop-1.6.10.jar:na] 120 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:107) ~[runtime-desktop-1.6.10.jar:na] 121 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 122 | at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) ~[runtime-desktop-1.6.10.jar:na] 123 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1.invoke(Application.desktop.kt:223) ~[ui-desktop-1.6.10.jar:na] 124 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1.invoke(Application.desktop.kt:221) ~[ui-desktop-1.6.10.jar:na] 125 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:107) ~[runtime-desktop-1.6.10.jar:na] 126 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 127 | at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:33) ~[runtime-desktop-1.6.10.jar:na] 128 | at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3303) ~[runtime-desktop-1.6.10.jar:na] 129 | at androidx.compose.runtime.ComposerImpl.composeContent$runtime(Composer.kt:3236) ~[runtime-desktop-1.6.10.jar:na] 130 | at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:725) ~[runtime-desktop-1.6.10.jar:na] 131 | at androidx.compose.runtime.Recomposer.composeInitial$runtime(Recomposer.kt:1071) ~[runtime-desktop-1.6.10.jar:na] 132 | at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:633) ~[runtime-desktop-1.6.10.jar:na] 133 | at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:619) ~[runtime-desktop-1.6.10.jar:na] 134 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2.invokeSuspend(Application.desktop.kt:221) ~[ui-desktop-1.6.10.jar:na] 135 | at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-2.0.0.jar:2.0.0-release-341] 136 | at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) ~[kotlinx-coroutines-core-jvm-1.8.1.jar:na] 137 | at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318) ~[na:na] 138 | at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773) ~[na:na] 139 | at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720) ~[na:na] 140 | at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714) ~[na:na] 141 | at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) ~[na:na] 142 | at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) ~[na:na] 143 | at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742) ~[na:na] 144 | at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) ~[na:na] 145 | at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) ~[na:na] 146 | at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) ~[na:na] 147 | at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) ~[na:na] 148 | at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) ~[na:na] 149 | at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) ~[na:na] 150 | Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persistence.jdbc.url' for common cases or 'hibernate.dialect' when a custom Dialect implementation must be provided) 151 | at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:276) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 152 | at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 153 | at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 154 | at org.hibernate.boot.model.relational.Database.(Database.java:45) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 155 | at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:221) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 156 | at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:189) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 157 | at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:171) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 158 | at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1431) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 159 | at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1502) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 160 | at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.1.8.jar:6.1.8] 161 | at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) ~[spring-orm-6.1.8.jar:6.1.8] 162 | at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.1.8.jar:6.1.8] 163 | at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.1.8.jar:6.1.8] 164 | at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) ~[spring-orm-6.1.8.jar:6.1.8] 165 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) ~[spring-beans-6.1.8.jar:6.1.8] 166 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) ~[spring-beans-6.1.8.jar:6.1.8] 167 | ... 53 common frames omitted 168 | Caused by: org.hibernate.HibernateException: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persistence.jdbc.url' for common cases or 'hibernate.dialect' when a custom Dialect implementation must be provided) 169 | at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:191) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 170 | at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:87) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 171 | at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentWithDefaults(JdbcEnvironmentInitiator.java:152) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 172 | at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:362) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 173 | at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:123) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 174 | at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:77) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 175 | at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 176 | at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final] 177 | ... 68 common frames omitted 178 | 179 | 2024-05-30T23:24:13.115+01:00 INFO 37288 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Starting application using Java 17.0.11 with PID 37288 (started by idali in /Users/idali/IdeaProjects/JtSpring2/composeApp) 180 | 2024-05-30T23:24:13.116+01:00 INFO 37288 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : No active profile set, falling back to 1 default profile: "default" 181 | 2024-05-30T23:24:13.264+01:00 INFO 37288 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 182 | 2024-05-30T23:24:13.281+01:00 INFO 37288 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 13 ms. Found 1 JPA repository interface. 183 | 2024-05-30T23:24:13.472+01:00 INFO 37288 --- [AWT-EventQueue-0] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 184 | 2024-05-30T23:24:13.493+01:00 INFO 37288 --- [AWT-EventQueue-0] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final 185 | 2024-05-30T23:24:13.505+01:00 INFO 37288 --- [AWT-EventQueue-0] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 186 | 2024-05-30T23:24:13.615+01:00 INFO 37288 --- [AWT-EventQueue-0] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 187 | 2024-05-30T23:24:13.623+01:00 INFO 37288 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 188 | 2024-05-30T23:24:13.787+01:00 INFO 37288 --- [AWT-EventQueue-0] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@2ff91511 189 | 2024-05-30T23:24:13.788+01:00 INFO 37288 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 190 | 2024-05-30T23:24:14.076+01:00 INFO 37288 --- [AWT-EventQueue-0] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 191 | 2024-05-30T23:24:14.125+01:00 INFO 37288 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 192 | 2024-05-30T23:24:14.188+01:00 WARN 37288 --- [AWT-EventQueue-0] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in URL [jar:file:/Users/idali/IdeaProjects/JtSpring2/composeApp/build/libs/composeApp-desktop.jar!/ixidev/jetspring/data/services/UserService.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'userRepository' defined in ixidev.jetspring.data.services.UserRepository defined in @EnableJpaRepositories declared on Main: kotlin/reflect/full/KClasses 193 | 2024-05-30T23:24:14.188+01:00 INFO 37288 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 194 | 2024-05-30T23:24:14.189+01:00 INFO 37288 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 195 | 2024-05-30T23:24:14.225+01:00 INFO 37288 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. 196 | 2024-05-30T23:24:14.228+01:00 INFO 37288 --- [AWT-EventQueue-0] .s.b.a.l.ConditionEvaluationReportLogger : 197 | 198 | Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 199 | 2024-05-30T23:24:14.234+01:00 ERROR 37288 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Application run failed 200 | 201 | org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in URL [jar:file:/Users/idali/IdeaProjects/JtSpring2/composeApp/build/libs/composeApp-desktop.jar!/ixidev/jetspring/data/services/UserService.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'userRepository' defined in ixidev.jetspring.data.services.UserRepository defined in @EnableJpaRepositories declared on Main: kotlin/reflect/full/KClasses 202 | at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795) ~[spring-beans-6.1.8.jar:6.1.8] 203 | at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:237) ~[spring-beans-6.1.8.jar:6.1.8] 204 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1357) ~[spring-beans-6.1.8.jar:6.1.8] 205 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1194) ~[spring-beans-6.1.8.jar:6.1.8] 206 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.8.jar:6.1.8] 207 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8] 208 | at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8] 209 | at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8] 210 | at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8] 211 | at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.8.jar:6.1.8] 212 | at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.8.jar:6.1.8] 213 | at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.8.jar:6.1.8] 214 | at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar:6.1.8] 215 | at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.0.jar:3.3.0] 216 | at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.0.jar:3.3.0] 217 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.0.jar:3.3.0] 218 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.0.jar:3.3.0] 219 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.0.jar:3.3.0] 220 | at ixidev.jetspring.MainKt$main$1.invoke(Main.kt:192) ~[composeApp-desktop.jar:na] 221 | at ixidev.jetspring.MainKt$main$1.invoke(Main.kt:55) ~[composeApp-desktop.jar:na] 222 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:116) ~[runtime-desktop-1.6.10.jar:na] 223 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 224 | at androidx.compose.ui.window.Application_desktopKt$application$1$1.invoke(Application.desktop.kt:117) ~[ui-desktop-1.6.10.jar:na] 225 | at androidx.compose.ui.window.Application_desktopKt$application$1$1.invoke(Application.desktop.kt:116) ~[ui-desktop-1.6.10.jar:na] 226 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:116) ~[runtime-desktop-1.6.10.jar:na] 227 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 228 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1$1.invoke(Application.desktop.kt:233) ~[ui-desktop-1.6.10.jar:na] 229 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1$1.invoke(Application.desktop.kt:232) ~[ui-desktop-1.6.10.jar:na] 230 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:107) ~[runtime-desktop-1.6.10.jar:na] 231 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 232 | at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) ~[runtime-desktop-1.6.10.jar:na] 233 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1.invoke(Application.desktop.kt:223) ~[ui-desktop-1.6.10.jar:na] 234 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1.invoke(Application.desktop.kt:221) ~[ui-desktop-1.6.10.jar:na] 235 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:107) ~[runtime-desktop-1.6.10.jar:na] 236 | at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jb.kt:33) ~[runtime-desktop-1.6.10.jar:na] 237 | at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:33) ~[runtime-desktop-1.6.10.jar:na] 238 | at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3303) ~[runtime-desktop-1.6.10.jar:na] 239 | at androidx.compose.runtime.ComposerImpl.composeContent$runtime(Composer.kt:3236) ~[runtime-desktop-1.6.10.jar:na] 240 | at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:725) ~[runtime-desktop-1.6.10.jar:na] 241 | at androidx.compose.runtime.Recomposer.composeInitial$runtime(Recomposer.kt:1071) ~[runtime-desktop-1.6.10.jar:na] 242 | at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:633) ~[runtime-desktop-1.6.10.jar:na] 243 | at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:619) ~[runtime-desktop-1.6.10.jar:na] 244 | at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2.invokeSuspend(Application.desktop.kt:221) ~[ui-desktop-1.6.10.jar:na] 245 | at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-2.0.0.jar:2.0.0-release-341] 246 | at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) ~[kotlinx-coroutines-core-jvm-1.8.1.jar:na] 247 | at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318) ~[na:na] 248 | at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773) ~[na:na] 249 | at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720) ~[na:na] 250 | at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714) ~[na:na] 251 | at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) ~[na:na] 252 | at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) ~[na:na] 253 | at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742) ~[na:na] 254 | at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) ~[na:na] 255 | at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) ~[na:na] 256 | at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) ~[na:na] 257 | at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) ~[na:na] 258 | at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) ~[na:na] 259 | at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) ~[na:na] 260 | Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository' defined in ixidev.jetspring.data.services.UserRepository defined in @EnableJpaRepositories declared on Main: kotlin/reflect/full/KClasses 261 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788) ~[spring-beans-6.1.8.jar:6.1.8] 262 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.8.jar:6.1.8] 263 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8] 264 | at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8] 265 | at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8] 266 | at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8] 267 | at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.8.jar:6.1.8] 268 | at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.1.8.jar:6.1.8] 269 | at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443) ~[spring-beans-6.1.8.jar:6.1.8] 270 | at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.8.jar:6.1.8] 271 | at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904) ~[spring-beans-6.1.8.jar:6.1.8] 272 | at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782) ~[spring-beans-6.1.8.jar:6.1.8] 273 | ... 57 common frames omitted 274 | Caused by: java.lang.NoClassDefFoundError: kotlin/reflect/full/KClasses 275 | at org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers$3.lambda$discover$5(PreferredConstructorDiscoverer.java:202) ~[spring-data-commons-3.3.0.jar:3.3.0] 276 | at java.base/java.util.Optional.orElseGet(Optional.java:364) ~[na:na] 277 | at org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers$3.discover(PreferredConstructorDiscoverer.java:199) ~[spring-data-commons-3.3.0.jar:3.3.0] 278 | at org.springframework.data.mapping.model.PreferredConstructorDiscoverer.discover(PreferredConstructorDiscoverer.java:84) ~[spring-data-commons-3.3.0.jar:3.3.0] 279 | at org.springframework.data.mapping.model.InstanceCreatorMetadataDiscoverer.discover(InstanceCreatorMetadataDiscoverer.java:116) ~[spring-data-commons-3.3.0.jar:3.3.0] 280 | at org.springframework.data.mapping.model.BasicPersistentEntity.(BasicPersistentEntity.java:116) ~[spring-data-commons-3.3.0.jar:3.3.0] 281 | at org.springframework.data.jpa.mapping.JpaPersistentEntityImpl.(JpaPersistentEntityImpl.java:59) ~[spring-data-jpa-3.3.0.jar:3.3.0] 282 | at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.createPersistentEntity(JpaMetamodelMappingContext.java:66) ~[spring-data-jpa-3.3.0.jar:3.3.0] 283 | at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.createPersistentEntity(JpaMetamodelMappingContext.java:1) ~[spring-data-jpa-3.3.0.jar:3.3.0] 284 | at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:448) ~[spring-data-commons-3.3.0.jar:3.3.0] 285 | at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:424) ~[spring-data-commons-3.3.0.jar:3.3.0] 286 | at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:320) ~[spring-data-commons-3.3.0.jar:3.3.0] 287 | at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:246) ~[spring-data-commons-3.3.0.jar:3.3.0] 288 | at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:97) ~[spring-data-commons-3.3.0.jar:3.3.0] 289 | at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$6(RepositoryFactoryBeanSupport.java:289) ~[spring-data-commons-3.3.0.jar:3.3.0] 290 | at java.base/java.util.Optional.ifPresent(Optional.java:178) ~[na:na] 291 | at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:289) ~[spring-data-commons-3.3.0.jar:3.3.0] 292 | at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:132) ~[spring-data-jpa-3.3.0.jar:3.3.0] 293 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) ~[spring-beans-6.1.8.jar:6.1.8] 294 | at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) ~[spring-beans-6.1.8.jar:6.1.8] 295 | ... 68 common frames omitted 296 | Caused by: java.lang.ClassNotFoundException: kotlin.reflect.full.KClasses 297 | at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na] 298 | at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na] 299 | at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[na:na] 300 | ... 88 common frames omitted 301 | 302 | 2024-05-30T23:25:42.026+01:00 INFO 37306 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Starting application using Java 17.0.11 with PID 37306 (started by idali in /Users/idali/IdeaProjects/JtSpring2/composeApp) 303 | 2024-05-30T23:25:42.027+01:00 INFO 37306 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : No active profile set, falling back to 1 default profile: "default" 304 | 2024-05-30T23:25:42.181+01:00 INFO 37306 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 305 | 2024-05-30T23:25:42.199+01:00 INFO 37306 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 1 JPA repository interface. 306 | 2024-05-30T23:25:42.475+01:00 INFO 37306 --- [AWT-EventQueue-0] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 307 | 2024-05-30T23:25:42.495+01:00 INFO 37306 --- [AWT-EventQueue-0] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final 308 | 2024-05-30T23:25:42.507+01:00 INFO 37306 --- [AWT-EventQueue-0] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 309 | 2024-05-30T23:25:42.613+01:00 INFO 37306 --- [AWT-EventQueue-0] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 310 | 2024-05-30T23:25:42.621+01:00 INFO 37306 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 311 | 2024-05-30T23:25:42.755+01:00 INFO 37306 --- [AWT-EventQueue-0] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@684d29ae 312 | 2024-05-30T23:25:42.755+01:00 INFO 37306 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 313 | 2024-05-30T23:25:43.043+01:00 INFO 37306 --- [AWT-EventQueue-0] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 314 | 2024-05-30T23:25:43.079+01:00 INFO 37306 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 315 | 2024-05-30T23:25:43.258+01:00 INFO 37306 --- [AWT-EventQueue-0] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsServiceImpl 316 | 2024-05-30T23:25:43.316+01:00 INFO 37306 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Started application in 1.432 seconds (process running for 1.898) 317 | 2024-05-30T23:25:43.410+01:00 INFO 37306 --- [AWT-EventQueue-0] ixidev.jetspring.data.DataGenerator : Generating Admin Data 318 | 2024-05-30T23:25:43.502+01:00 INFO 37306 --- [AWT-EventQueue-0] ixidev.jetspring.data.DataGenerator : Admin credential info generated 319 | 2024-05-30T23:26:35.475+01:00 INFO 37306 --- [SpringApplicationShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 320 | 2024-05-30T23:26:35.477+01:00 INFO 37306 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 321 | 2024-05-30T23:26:35.484+01:00 INFO 37306 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. 322 | 2024-05-30T23:29:29.677+01:00 INFO 37365 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Starting application using Java 17.0.11 with PID 37365 (started by idali in /Users/idali/IdeaProjects/JetSpring/composeApp) 323 | 2024-05-30T23:29:29.679+01:00 INFO 37365 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : No active profile set, falling back to 1 default profile: "default" 324 | 2024-05-30T23:29:29.831+01:00 INFO 37365 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 325 | 2024-05-30T23:29:29.848+01:00 INFO 37365 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 1 JPA repository interface. 326 | 2024-05-30T23:29:30.139+01:00 INFO 37365 --- [AWT-EventQueue-0] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 327 | 2024-05-30T23:29:30.167+01:00 INFO 37365 --- [AWT-EventQueue-0] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final 328 | 2024-05-30T23:29:30.184+01:00 INFO 37365 --- [AWT-EventQueue-0] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 329 | 2024-05-30T23:29:30.287+01:00 INFO 37365 --- [AWT-EventQueue-0] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 330 | 2024-05-30T23:29:30.295+01:00 INFO 37365 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 331 | 2024-05-30T23:29:30.425+01:00 INFO 37365 --- [AWT-EventQueue-0] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@141dd458 332 | 2024-05-30T23:29:30.426+01:00 INFO 37365 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 333 | 2024-05-30T23:29:30.712+01:00 INFO 37365 --- [AWT-EventQueue-0] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 334 | 2024-05-30T23:29:30.745+01:00 INFO 37365 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 335 | 2024-05-30T23:29:30.928+01:00 INFO 37365 --- [AWT-EventQueue-0] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsServiceImpl 336 | 2024-05-30T23:29:30.982+01:00 INFO 37365 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Started application in 1.451 seconds (process running for 1.925) 337 | 2024-05-30T23:29:31.080+01:00 INFO 37365 --- [AWT-EventQueue-0] ixidev.jetspring.data.DataGenerator : Using existing database 338 | 2024-05-30T23:29:34.194+01:00 INFO 37365 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 339 | 2024-05-30T23:29:34.199+01:00 INFO 37365 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 340 | 2024-05-30T23:29:34.212+01:00 INFO 37365 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. 341 | 2024-05-30T23:30:08.981+01:00 INFO 37388 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Starting application using Java 17.0.11 with PID 37388 (started by idali in /Users/idali/IdeaProjects/JetSpring/composeApp) 342 | 2024-05-30T23:30:08.982+01:00 INFO 37388 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : No active profile set, falling back to 1 default profile: "default" 343 | 2024-05-30T23:30:09.131+01:00 INFO 37388 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 344 | 2024-05-30T23:30:09.149+01:00 INFO 37388 --- [AWT-EventQueue-0] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 14 ms. Found 1 JPA repository interface. 345 | 2024-05-30T23:30:09.456+01:00 INFO 37388 --- [AWT-EventQueue-0] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 346 | 2024-05-30T23:30:09.477+01:00 INFO 37388 --- [AWT-EventQueue-0] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final 347 | 2024-05-30T23:30:09.489+01:00 INFO 37388 --- [AWT-EventQueue-0] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 348 | 2024-05-30T23:30:09.590+01:00 INFO 37388 --- [AWT-EventQueue-0] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 349 | 2024-05-30T23:30:09.599+01:00 INFO 37388 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 350 | 2024-05-30T23:30:09.734+01:00 INFO 37388 --- [AWT-EventQueue-0] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@55e45f93 351 | 2024-05-30T23:30:09.735+01:00 INFO 37388 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 352 | 2024-05-30T23:30:10.022+01:00 INFO 37388 --- [AWT-EventQueue-0] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 353 | 2024-05-30T23:30:10.063+01:00 INFO 37388 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 354 | 2024-05-30T23:30:10.247+01:00 INFO 37388 --- [AWT-EventQueue-0] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name userDetailsServiceImpl 355 | 2024-05-30T23:30:10.302+01:00 INFO 37388 --- [AWT-EventQueue-0] o.s.boot.SpringApplication : Started application in 1.469 seconds (process running for 1.932) 356 | 2024-05-30T23:30:10.398+01:00 INFO 37388 --- [AWT-EventQueue-0] ixidev.jetspring.data.DataGenerator : Using existing database 357 | 2024-05-30T23:30:12.779+01:00 INFO 37388 --- [AWT-EventQueue-0] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default' 358 | 2024-05-30T23:30:12.783+01:00 INFO 37388 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 359 | 2024-05-30T23:30:12.795+01:00 INFO 37388 --- [AWT-EventQueue-0] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. 360 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/composeResources/drawable/compose-multiplatform.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 18 | 24 | 30 | 36 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress( 2 | "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", 3 | "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", 4 | "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", 5 | "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", 6 | "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused", "unused" 7 | ) 8 | 9 | package ixidev.jetspring.base 10 | 11 | import kotlinx.coroutines.* 12 | 13 | /*** 14 | * Created by ixiDev on 19/08/2022 15 | * GitHub : https://www.github.com/ixiDev 16 | **/ 17 | 18 | 19 | @Suppress("unused") 20 | abstract class BaseViewModel { 21 | 22 | private val viewModelScope: CoroutineScope = CoroutineScope(Job()) 23 | 24 | protected fun launchOnMain(block: suspend CoroutineScope.() -> Unit): Job { 25 | return viewModelScope.launch(Dispatchers.Main) { block() } 26 | } 27 | 28 | protected fun launchOnIO(block: suspend CoroutineScope.() -> Unit): Job { 29 | return viewModelScope.launch(Dispatchers.IO) { block() } 30 | } 31 | 32 | protected fun onClear() { 33 | if (viewModelScope.isActive) { 34 | viewModelScope.cancel() 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/DataGenerator.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data 2 | 3 | 4 | import ixidev.jetspring.data.entities.Role 5 | import ixidev.jetspring.data.entities.User 6 | import ixidev.jetspring.data.services.UserRepository 7 | import org.slf4j.LoggerFactory 8 | import org.springframework.boot.CommandLineRunner 9 | import org.springframework.context.annotation.Bean 10 | import org.springframework.security.crypto.password.PasswordEncoder 11 | import org.springframework.stereotype.Component 12 | 13 | @Component 14 | class DataGenerator { 15 | @Bean 16 | fun loadData(passwordEncoder: PasswordEncoder, userRepository: UserRepository): CommandLineRunner { 17 | return CommandLineRunner { 18 | val logger = LoggerFactory.getLogger(javaClass) 19 | if (userRepository.count() != 0L) { 20 | logger.info("Using existing database") 21 | return@CommandLineRunner 22 | } 23 | logger.info("Generating Admin Data") 24 | val admin = User() 25 | admin.name = "Admin" 26 | admin.username = "admin@jetspring.com" 27 | admin.hashedPassword = passwordEncoder.encode("admin") 28 | admin.roles = mutableSetOf(Role.USER, Role.ADMIN) 29 | userRepository.save(admin) 30 | logger.info("Admin credential info generated") 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/DataService.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data 2 | 3 | import org.springframework.stereotype.Component 4 | 5 | /*** 6 | * Created by ixiDev on 22/08/2022 7 | * GitHub : https://www.github.com/ixiDev 8 | **/ 9 | @Component 10 | class DataService { 11 | suspend fun getCount(): Int { 12 | return 0 13 | } 14 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/entities/AbstractEntity.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data.entities 2 | 3 | 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.Id 6 | import jakarta.persistence.MappedSuperclass 7 | import org.hibernate.annotations.Type 8 | import java.util.* 9 | 10 | @MappedSuperclass 11 | abstract class AbstractEntity { 12 | @Id 13 | @GeneratedValue 14 | // @Type("uuid-char") 15 | var id: UUID? = null 16 | override fun hashCode(): Int { 17 | return if (id != null) { 18 | id.hashCode() 19 | } else super.hashCode() 20 | } 21 | 22 | override fun equals(other: Any?): Boolean { 23 | if (other !is AbstractEntity) { 24 | return false // null or other class 25 | } 26 | return if (id != null) { 27 | id == other.id 28 | } else super.equals(other) 29 | } 30 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/entities/BaseEntity.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data.entities 2 | 3 | import jakarta.persistence.GeneratedValue 4 | import jakarta.persistence.Id 5 | import jakarta.persistence.MappedSuperclass 6 | 7 | /*** 8 | * Created by ixiDev on 21/08/2022 9 | * GitHub : https://www.github.com/ixiDev 10 | **/ 11 | @MappedSuperclass 12 | abstract class BaseEntity { 13 | @Id 14 | @GeneratedValue 15 | var id: Long? = null 16 | override fun equals(other: Any?): Boolean { 17 | if (this === other) return true 18 | if (other !is BaseEntity) return false 19 | 20 | if (id != other.id) return false 21 | 22 | return true 23 | } 24 | 25 | override fun hashCode(): Int { 26 | return id?.hashCode() ?: 0 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/entities/Role.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data.entities 2 | 3 | enum class Role { 4 | USER, ADMIN 5 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/entities/User.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data.entities 2 | 3 | import jakarta.persistence.* 4 | 5 | @Entity 6 | @Table(name = "application_user") 7 | class User( 8 | var username: String? = null, 9 | var name: String? = null, 10 | var hashedPassword: String? = null, 11 | @Enumerated(EnumType.STRING) 12 | @ElementCollection(fetch = FetchType.EAGER) 13 | var roles: MutableSet = mutableSetOf() 14 | ) : AbstractEntity() -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/services/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data.services 2 | 3 | import ixidev.jetspring.data.entities.User 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import java.util.* 6 | 7 | interface UserRepository : JpaRepository { 8 | fun findByUsername(username: String?): User? 9 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/data/services/UserService.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.data.services 2 | 3 | import ixidev.jetspring.data.entities.User 4 | import org.springframework.beans.factory.annotation.Autowired 5 | import org.springframework.data.domain.Page 6 | import org.springframework.data.domain.Pageable 7 | import org.springframework.stereotype.Service 8 | import java.util.* 9 | 10 | @Service 11 | class UserService @Autowired constructor(private val repository: UserRepository) { 12 | operator fun get(id: UUID): Optional { 13 | return repository.findById(id) 14 | } 15 | 16 | fun update(entity: User): User { 17 | return repository.save(entity) 18 | } 19 | 20 | fun delete(id: UUID) { 21 | repository.deleteById(id) 22 | } 23 | 24 | fun list(pageable: Pageable?): Page { 25 | return repository.findAll(pageable!!) 26 | } 27 | 28 | fun count(): Int { 29 | return repository.count().toInt() 30 | } 31 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/security/AppSecurityConfig.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.security 2 | 3 | import org.springframework.context.annotation.Bean 4 | import org.springframework.context.annotation.Configuration 5 | import org.springframework.context.support.ResourceBundleMessageSource 6 | import org.springframework.security.authentication.AuthenticationManager 7 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration 8 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder 9 | import org.springframework.security.crypto.password.PasswordEncoder 10 | 11 | 12 | /*** 13 | * Created by ixiDev on 19/08/2022 14 | * GitHub : https://www.github.com/ixiDev 15 | **/ 16 | @Configuration 17 | class AppSecurityConfig { 18 | 19 | 20 | @Bean 21 | fun authenticationManagerBean(builder: AuthenticationConfiguration): AuthenticationManager { 22 | return builder.authenticationManager 23 | } 24 | 25 | @Bean 26 | fun passwordEncoder(): PasswordEncoder { 27 | return BCryptPasswordEncoder() 28 | } 29 | 30 | 31 | @Bean 32 | fun messageSource(): ResourceBundleMessageSource { 33 | val rs = ResourceBundleMessageSource() 34 | rs.setBasename("strings") 35 | rs.setDefaultEncoding("UTF-8") 36 | rs.setUseCodeAsDefaultMessage(true) 37 | return rs 38 | } 39 | 40 | 41 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/security/AuthenticatedUser.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.security 2 | 3 | 4 | 5 | import ixidev.jetspring.data.entities.User 6 | import ixidev.jetspring.data.services.UserRepository 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.security.authentication.AnonymousAuthenticationToken 9 | import org.springframework.security.core.Authentication 10 | import org.springframework.security.core.context.SecurityContextHolder 11 | import org.springframework.stereotype.Component 12 | import java.util.* 13 | 14 | @Component 15 | class AuthenticatedUser @Autowired constructor( 16 | private val userRepository: UserRepository, 17 | private val authSate: UserAuthSate 18 | ) { 19 | 20 | private val authentication: Optional 21 | get() { 22 | val context = SecurityContextHolder.getContext() 23 | val auth = context.authentication 24 | return Optional.ofNullable(auth) 25 | .filter { authentication: Authentication? -> authentication !is AnonymousAuthenticationToken } 26 | } 27 | 28 | fun get(): Optional { 29 | return authentication.map { authentication: Authentication -> 30 | userRepository.findByUsername(authentication.name) 31 | } 32 | } 33 | 34 | fun logout() { 35 | val context = SecurityContextHolder.getContext() 36 | SecurityContextHolder.clearContext() 37 | context.authentication = null 38 | authSate.auth.tryEmit(false) 39 | } 40 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/security/UserAuthSate.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.security 2 | 3 | import kotlinx.coroutines.flow.MutableStateFlow 4 | import org.springframework.stereotype.Component 5 | 6 | /*** 7 | * Created by ixiDev on 20/08/2022 8 | * GitHub : https://www.github.com/ixiDev 9 | **/ 10 | 11 | @Component 12 | class UserAuthSate { 13 | var auth: MutableStateFlow = MutableStateFlow(false) 14 | var error = MutableStateFlow(null) 15 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/security/UserDetailsServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.security 2 | 3 | 4 | import ixidev.jetspring.data.entities.Role 5 | import ixidev.jetspring.data.entities.User 6 | import ixidev.jetspring.data.services.UserRepository 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.security.core.GrantedAuthority 9 | import org.springframework.security.core.authority.SimpleGrantedAuthority 10 | import org.springframework.security.core.userdetails.UserDetails 11 | import org.springframework.security.core.userdetails.UserDetailsService 12 | import org.springframework.security.core.userdetails.UsernameNotFoundException 13 | import org.springframework.stereotype.Service 14 | import java.util.stream.Collectors 15 | 16 | @Service 17 | //@EnableGlobalAuthentication 18 | class UserDetailsServiceImpl @Autowired constructor( 19 | private val userRepository: UserRepository 20 | ) : UserDetailsService { 21 | @Throws(UsernameNotFoundException::class) 22 | override fun loadUserByUsername(username: String): UserDetails { 23 | val user = userRepository.findByUsername(username) 24 | return if (user == null) { 25 | throw UsernameNotFoundException("No user present with username: $username") 26 | } else { 27 | org.springframework.security.core.userdetails.User( 28 | user.username, user.hashedPassword, 29 | getAuthorities(user) 30 | ) 31 | } 32 | } 33 | 34 | companion object { 35 | private fun getAuthorities(user: User): List { 36 | return user.roles.stream().map { role: Role -> 37 | SimpleGrantedAuthority( 38 | "ROLE_$role" 39 | ) 40 | }.collect(Collectors.toList()) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/GlobalViewModel.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui 2 | 3 | import ixidev.jetspring.base.BaseViewModel 4 | import ixidev.jetspring.security.AuthenticatedUser 5 | import org.springframework.stereotype.Component 6 | 7 | /*** 8 | * Created by ixiDev on 20/08/2022 9 | * GitHub : https://www.github.com/ixiDev 10 | **/ 11 | 12 | @Component 13 | class GlobalViewModel( 14 | private val user: AuthenticatedUser 15 | ) : BaseViewModel() { 16 | 17 | fun getUserName(): String { 18 | val userOptional = user.get() 19 | return if (userOptional.isPresent) 20 | userOptional.get().name ?: "Unknown" 21 | else "Unknown" 22 | } 23 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/auth/LoginScreen.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalComposeUiApi::class) 2 | 3 | package ixidev.jetspring.ui.auth 4 | 5 | import androidx.compose.foundation.Image 6 | import androidx.compose.foundation.layout.* 7 | import androidx.compose.foundation.shape.CircleShape 8 | import androidx.compose.material.Button 9 | import androidx.compose.material.Icon 10 | import androidx.compose.material.Text 11 | import androidx.compose.material.TextField 12 | import androidx.compose.runtime.* 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.ExperimentalComposeUiApi 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.draw.clip 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.input.key.Key 19 | import androidx.compose.ui.input.key.key 20 | import androidx.compose.ui.input.key.onKeyEvent 21 | import androidx.compose.ui.platform.testTag 22 | import androidx.compose.ui.res.painterResource 23 | import androidx.compose.ui.text.font.FontWeight 24 | import androidx.compose.ui.text.input.PasswordVisualTransformation 25 | import androidx.compose.ui.unit.dp 26 | import androidx.compose.ui.unit.sp 27 | import compose.icons.FontAwesomeIcons 28 | import compose.icons.fontawesomeicons.Solid 29 | import compose.icons.fontawesomeicons.solid.Key 30 | import compose.icons.fontawesomeicons.solid.User 31 | import ixidev.jetspring.utils.stringResource 32 | 33 | /*** 34 | * Created by ixiDev on 19/08/2022 35 | * GitHub : https://www.github.com/ixiDev 36 | **/ 37 | 38 | 39 | @Composable 40 | fun LoginScreen(viewModel: LoginViewModel) { 41 | Column( 42 | modifier = Modifier.fillMaxSize(), 43 | verticalArrangement = Arrangement.Center, 44 | horizontalAlignment = Alignment.CenterHorizontally 45 | 46 | ) { 47 | 48 | Image( 49 | painter = painterResource("app-icon.svg"), 50 | contentDescription = "app-logo", 51 | modifier = Modifier.size( 52 | 80.dp 53 | ).clip(CircleShape) 54 | ) 55 | Spacer(modifier = Modifier.height(8.dp)) 56 | Text( 57 | text = "JetSpring", 58 | fontSize = 23.sp, 59 | fontWeight = FontWeight.Bold 60 | ) 61 | Spacer(modifier = Modifier.height(50.dp)) 62 | 63 | LoginForm(viewModel) 64 | 65 | val error by viewModel.errorState.collectAsState() 66 | if (!error.isNullOrEmpty()) { 67 | Spacer(modifier = Modifier.height(15.dp)) 68 | Text( 69 | text = stringResource("global.login.error"), 70 | fontSize = 18.sp, 71 | color = Color.Red 72 | ) 73 | } 74 | } 75 | 76 | } 77 | 78 | @OptIn(ExperimentalComposeUiApi::class) 79 | @Composable 80 | private fun LoginForm(viewModel: LoginViewModel) { 81 | var userName by remember { mutableStateOf("") } 82 | var password by remember { mutableStateOf("") } 83 | TextField( 84 | value = userName, 85 | onValueChange = { 86 | userName = it 87 | viewModel.emitError(null) 88 | }, 89 | singleLine = true, 90 | label = { 91 | Text(stringResource("global.username")) 92 | }, 93 | leadingIcon = { 94 | Icon( 95 | modifier = Modifier.size(24.dp), 96 | imageVector = FontAwesomeIcons.Solid.User, 97 | contentDescription = null 98 | ) 99 | }, 100 | modifier = Modifier.testTag("username") 101 | ) 102 | Spacer(modifier = Modifier.height(8.dp)) 103 | TextField( 104 | value = password, 105 | onValueChange = { 106 | password = it 107 | viewModel.emitError(null) 108 | }, 109 | singleLine = true, 110 | label = { 111 | Text(stringResource("global.password")) 112 | }, 113 | leadingIcon = { 114 | Icon( 115 | modifier = Modifier.size(24.dp), 116 | imageVector = FontAwesomeIcons.Solid.Key, 117 | contentDescription = null 118 | ) 119 | }, 120 | visualTransformation = PasswordVisualTransformation(), 121 | modifier = Modifier.onKeyEvent { 122 | if (it.key == Key.Enter) { 123 | viewModel.login(userName, password) 124 | true 125 | } else { 126 | false 127 | } 128 | }.testTag("password") 129 | ) 130 | Spacer(modifier = Modifier.height(8.dp)) 131 | 132 | Button( 133 | modifier = Modifier.testTag("login-button"), 134 | onClick = { 135 | viewModel.login(userName, password) 136 | } 137 | ) { 138 | Text(text = stringResource("global.login")) 139 | } 140 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/auth/LoginViewModel.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.auth 2 | 3 | import ixidev.jetspring.base.BaseViewModel 4 | import ixidev.jetspring.security.UserAuthSate 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.security.authentication.AuthenticationManager 7 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken 8 | import org.springframework.security.core.Authentication 9 | import org.springframework.security.core.context.SecurityContextHolder 10 | import org.springframework.stereotype.Component 11 | 12 | 13 | /*** 14 | * Created by ixiDev on 19/08/2022 15 | * GitHub : https://www.github.com/ixiDev 16 | **/ 17 | @Component 18 | class LoginViewModel @Autowired constructor( 19 | private val authManager: AuthenticationManager, 20 | private val authSate: UserAuthSate, 21 | ) : BaseViewModel() { 22 | val errorState = authSate.error 23 | 24 | fun login(user: String?, pass: String?) { 25 | launchOnIO { 26 | try { 27 | val authReq = UsernamePasswordAuthenticationToken(user, pass) 28 | val auth: Authentication = authManager.authenticate(authReq) 29 | val sc = SecurityContextHolder.getContext() 30 | sc.authentication = auth 31 | authSate.auth.emit(auth.isAuthenticated) 32 | } catch (e: Exception) { 33 | authSate.error.emit(e.message) 34 | } 35 | } 36 | } 37 | 38 | fun emitError(error: String?) { 39 | launchOnIO { 40 | authSate.error.emit(null) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/components/AppMenuHeader.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.components 2 | 3 | import androidx.compose.foundation.layout.* 4 | import androidx.compose.material.Icon 5 | import androidx.compose.material.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.res.painterResource 10 | import androidx.compose.ui.text.font.FontWeight 11 | import androidx.compose.ui.unit.dp 12 | import androidx.compose.ui.unit.sp 13 | import ixidev.jetspring.utils.stringResource 14 | 15 | /*** 16 | * Created by ixiDev on 21/08/2022 17 | * GitHub : https://www.github.com/ixiDev 18 | **/ 19 | @Composable 20 | fun AppMenuHeader() { 21 | Column( 22 | verticalArrangement = Arrangement.Center, 23 | horizontalAlignment = Alignment.CenterHorizontally, 24 | modifier = Modifier.padding(10.dp) 25 | ) { 26 | Icon( 27 | contentDescription = null, 28 | modifier = Modifier.size(50.dp), 29 | painter = painterResource("app-icon.svg") 30 | ) 31 | Spacer(modifier = Modifier.height(8.dp)) 32 | Text( 33 | text = stringResource("global.app.name"), 34 | fontWeight = FontWeight.Bold, 35 | fontSize = 20.sp 36 | ) 37 | Spacer(modifier = Modifier.height(8.dp)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/components/AppWindowTitleB.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.* 5 | import androidx.compose.foundation.shape.CircleShape 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.foundation.window.WindowDraggableArea 8 | import androidx.compose.material.* 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.clip 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.semantics.Role 15 | import androidx.compose.ui.unit.dp 16 | import androidx.compose.ui.window.WindowPlacement 17 | import androidx.compose.ui.window.WindowScope 18 | import androidx.compose.ui.window.WindowState 19 | import compose.icons.FontAwesomeIcons 20 | import compose.icons.fontawesomeicons.Regular 21 | import compose.icons.fontawesomeicons.Solid 22 | import compose.icons.fontawesomeicons.regular.WindowClose 23 | import compose.icons.fontawesomeicons.regular.WindowMaximize 24 | import compose.icons.fontawesomeicons.solid.Moon 25 | import compose.icons.fontawesomeicons.solid.Sun 26 | import compose.icons.fontawesomeicons.solid.UserCircle 27 | import ixidev.jetspring.ui.GlobalViewModel 28 | import ixidev.jetspring.ui.theme.AppThemeState 29 | 30 | /*** 31 | * Created by ixiDev on 20/08/2022 32 | * GitHub : https://www.github.com/ixiDev 33 | **/ 34 | 35 | @Composable 36 | fun WindowScope.AppWindowTitleBar( 37 | viewModel: GlobalViewModel, 38 | windowState: WindowState, 39 | themeState: AppThemeState, 40 | onClose: () -> Unit 41 | ) { 42 | WindowDraggableArea( 43 | modifier = Modifier.fillMaxWidth() 44 | .height(48.dp) 45 | .background(MaterialTheme.colors.primary) 46 | ) { 47 | Box( 48 | modifier = Modifier.fillMaxSize(), 49 | ) { 50 | WindowOptions( 51 | Modifier.align(Alignment.CenterStart), 52 | onClose, windowState 53 | ) 54 | Row(modifier = Modifier.align(Alignment.CenterEnd)) { 55 | 56 | DarkModeButton(themeState) 57 | Spacer(modifier = Modifier.width(8.dp)) 58 | Card( 59 | shape = RoundedCornerShape(10.dp), 60 | modifier = Modifier.padding(5.dp) 61 | ) { 62 | 63 | Row( 64 | modifier = Modifier 65 | .padding(2.dp) 66 | .fillMaxHeight(), 67 | verticalAlignment = Alignment.CenterVertically, 68 | horizontalArrangement = Arrangement.Center 69 | ) { 70 | Text( 71 | text = viewModel.getUserName(), 72 | ) 73 | Spacer(modifier = Modifier.width(4.dp)) 74 | Icon( 75 | imageVector = FontAwesomeIcons.Solid.UserCircle, 76 | contentDescription = null, 77 | modifier = Modifier 78 | .size(24.dp) 79 | .clip(CircleShape) 80 | ) 81 | } 82 | } 83 | 84 | } 85 | } 86 | } 87 | } 88 | 89 | @OptIn(ExperimentalMaterialApi::class) 90 | @Composable 91 | private fun DarkModeButton(themeState: AppThemeState) { 92 | 93 | Card( 94 | onClick = { 95 | themeState.isDarkTheme = !themeState.isDarkTheme 96 | }, 97 | modifier = Modifier 98 | 99 | .fillMaxHeight() 100 | .aspectRatio(1f) 101 | .padding(5.dp), 102 | shape = CircleShape 103 | ) { 104 | val tint = if (themeState.isDarkTheme) Color.Yellow else Color.DarkGray 105 | Icon( 106 | imageVector = if ( 107 | themeState.isDarkTheme 108 | ) FontAwesomeIcons.Solid.Sun else FontAwesomeIcons.Solid.Moon, 109 | contentDescription = null, 110 | modifier = Modifier 111 | .padding(4.dp) 112 | .size(24.dp), 113 | tint = tint, 114 | ) 115 | } 116 | // IconButton( 117 | // onClick = { 118 | // themeState.isDarkTheme = !themeState.isDarkTheme 119 | // }, 120 | // modifier = Modifier.background( 121 | // MaterialTheme.colors.onPrimary, 122 | // CircleShape 123 | // ).size(26.dp) 124 | // ) { 125 | // 126 | // } 127 | } 128 | 129 | @Composable 130 | private fun WindowOptions( 131 | modifier: Modifier, 132 | onClose: () -> Unit, 133 | windowState: WindowState 134 | ) { 135 | Row(modifier = modifier) { 136 | IconButton( 137 | onClick = onClose 138 | ) { 139 | Icon( 140 | imageVector = FontAwesomeIcons.Regular.WindowClose, 141 | contentDescription = null, 142 | modifier = Modifier.size(24.dp), 143 | tint = MaterialTheme.colors.onPrimary 144 | ) 145 | } 146 | 147 | IconButton( 148 | onClick = { 149 | windowState.placement = if (windowState.placement == WindowPlacement.Floating) 150 | WindowPlacement.Maximized 151 | else WindowPlacement.Floating 152 | } 153 | ) { 154 | Icon( 155 | imageVector = FontAwesomeIcons.Regular.WindowMaximize, 156 | contentDescription = null, 157 | modifier = Modifier.size(24.dp), 158 | tint = MaterialTheme.colors.onPrimary 159 | ) 160 | } 161 | } 162 | } 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/components/NavigationMenuItem.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.components 2 | 3 | import androidx.compose.foundation.interaction.MutableInteractionSource 4 | import androidx.compose.foundation.layout.* 5 | import androidx.compose.foundation.selection.selectable 6 | import androidx.compose.material.* 7 | import androidx.compose.material.ripple.rememberRipple 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.remember 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.compose.ui.graphics.vector.ImageVector 13 | import androidx.compose.ui.semantics.Role 14 | import androidx.compose.ui.unit.dp 15 | 16 | /*** 17 | * Created by ixiDev on 19/08/2022 18 | * GitHub : https://www.github.com/ixiDev 19 | **/ 20 | 21 | @Composable 22 | fun NavigationMenuItem( 23 | selected: Boolean, 24 | onClick: () -> Unit, 25 | modifier: Modifier = Modifier, 26 | enabled: Boolean = true, 27 | icon: ImageVector, 28 | label: String, 29 | interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, 30 | selectedContentColor: Color = MaterialTheme.colors.primary, 31 | unselectedContentColor: Color = LocalContentColor.current.copy(alpha = ContentAlpha.medium) 32 | ) { 33 | val ripple = rememberRipple( 34 | bounded = true, 35 | color = selectedContentColor 36 | ) 37 | Row( 38 | modifier = modifier 39 | .selectable( 40 | selected = selected, 41 | onClick = onClick, 42 | enabled = enabled, 43 | role = Role.Button, 44 | interactionSource = interactionSource, 45 | indication = ripple 46 | ).padding(10.dp), 47 | ) { 48 | 49 | Icon( 50 | imageVector = icon, 51 | contentDescription = label, 52 | tint = if (selected) selectedContentColor else unselectedContentColor, 53 | modifier = Modifier.size(24.dp) 54 | ) 55 | Spacer(modifier = Modifier.width(10.dp)) 56 | Text( 57 | text = label, 58 | color = if (selected) selectedContentColor else unselectedContentColor 59 | ) 60 | } 61 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/components/SideBarMenu.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Spacer 5 | import androidx.compose.foundation.layout.fillMaxHeight 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.material.NavigationRail 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.getValue 11 | import androidx.compose.runtime.remember 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.unit.dp 15 | import ixidev.jetspring.ui.navigation.Screens 16 | import ixidev.jetspring.ui.navigation.navcontroller.NavController 17 | import ixidev.jetspring.utils.stringResource 18 | 19 | /*** 20 | * Created by ixiDev on 21/08/2022 21 | * GitHub : https://www.github.com/ixiDev 22 | **/ 23 | @Composable 24 | fun SideBarMenu(modifier: Modifier = Modifier, navController: NavController) { 25 | val screens = listOf( 26 | Screens.Home, 27 | Screens.Settings, 28 | Screens.NewWindow, 29 | ) 30 | val currentScreen by remember { 31 | navController.currentScreen 32 | } 33 | NavigationRail( 34 | modifier = modifier 35 | .fillMaxHeight() 36 | ) { 37 | AppMenuHeader() 38 | Spacer( 39 | modifier = Modifier 40 | .height(8.dp) 41 | .fillMaxWidth() 42 | .background(color = Color.DarkGray) 43 | ) 44 | screens.forEach { 45 | NavigationMenuItem( 46 | modifier = Modifier.fillMaxWidth(), 47 | selected = currentScreen == it, 48 | icon = it.icon!!, 49 | label = stringResource(it.label), 50 | onClick = { 51 | if (it is Screens.NewWindow) { 52 | navController.window(it) 53 | } else { 54 | navController.navigate(it) 55 | } 56 | }, 57 | ) 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/components/TextComponenets.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.components 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.Spacer 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.layout.height 7 | import androidx.compose.material.Text 8 | import androidx.compose.material.TextField 9 | import androidx.compose.runtime.* 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.awt.SwingPanel 12 | import androidx.compose.ui.unit.dp 13 | import com.github.lgooddatepicker.components.DatePicker 14 | import com.github.lgooddatepicker.components.DatePickerSettings 15 | import ixidev.jetspring.utils.isNumeric 16 | import java.time.LocalDate 17 | import java.util.* 18 | 19 | /*** 20 | * Created by ixiDev on 21/08/2022 21 | * GitHub : https://www.github.com/ixiDev 22 | **/ 23 | 24 | @Composable 25 | fun DateField( 26 | label: String, 27 | modifier: Modifier = Modifier, 28 | onDateChange: (date: LocalDate) -> Unit 29 | ) { 30 | val dataPicker = remember { 31 | DatePicker( 32 | DatePickerSettings( 33 | Locale.forLanguageTag("ar") 34 | ) 35 | ) 36 | } 37 | dataPicker.setDateToToday() 38 | dataPicker.addDateChangeListener { 39 | if (it.source.isValid) { 40 | onDateChange(it.newDate) 41 | } 42 | } 43 | Column(modifier) { 44 | Text(text = label) 45 | Spacer(modifier = Modifier.height(2.dp)) 46 | SwingPanel( 47 | modifier = Modifier.fillMaxWidth() 48 | .height(40.dp), 49 | factory = { dataPicker }, 50 | update = { 51 | 52 | } 53 | ) 54 | } 55 | 56 | } 57 | 58 | @Composable 59 | fun LabeledText( 60 | modifier: Modifier = Modifier, 61 | initValue: String = "", 62 | label: String, 63 | onTextChange: (text: String) -> Unit 64 | ) { 65 | var valueText by remember { mutableStateOf(initValue) } 66 | TextField( 67 | modifier = modifier, 68 | value = valueText, 69 | onValueChange = { 70 | valueText = it 71 | onTextChange(it) 72 | }, 73 | label = { 74 | Text(text = label) 75 | }, 76 | singleLine = true 77 | ) 78 | } 79 | 80 | @Composable 81 | fun LabeledNumericTextField( 82 | modifier: Modifier = Modifier, 83 | initValue: String = "", 84 | label: String, 85 | onTextChange: (text: String) -> Unit 86 | ) { 87 | var valueText by remember { mutableStateOf(initValue) } 88 | var error by remember { mutableStateOf(false) } 89 | TextField( 90 | modifier = modifier, 91 | value = valueText, 92 | onValueChange = { 93 | valueText = it 94 | error = !it.isNumeric() 95 | if (!error) 96 | onTextChange(it) 97 | }, 98 | label = { 99 | Text(text = label) 100 | }, 101 | isError = error, 102 | singleLine = true 103 | ) 104 | 105 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/components/ValidatorTextField.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.components 2 | 3 | import androidx.compose.material.TextField 4 | import androidx.compose.runtime.* 5 | import androidx.compose.ui.text.input.VisualTransformation 6 | 7 | /*** 8 | * Created by ixiDev on 19/08/2022 9 | * GitHub : https://www.github.com/ixiDev 10 | **/ 11 | 12 | 13 | @Composable 14 | fun ValidatorTextField( 15 | value: String, 16 | onValid: (value: String) -> Unit, 17 | singleLine: Boolean, 18 | label: @Composable () -> Unit, 19 | leadingIcon: @Composable () -> Unit, 20 | visualTransformation: VisualTransformation? = null, 21 | 22 | ) { 23 | 24 | var error by remember { mutableStateOf(false) } 25 | if (visualTransformation == null) 26 | TextField( 27 | value = value, 28 | onValueChange = { 29 | 30 | }, 31 | singleLine = singleLine, 32 | label = label, 33 | leadingIcon = leadingIcon, 34 | isError = error 35 | ) 36 | else { 37 | TextField( 38 | value = value, 39 | onValueChange = onValid, 40 | singleLine = singleLine, 41 | label = label, 42 | leadingIcon = leadingIcon, 43 | visualTransformation = visualTransformation 44 | ) 45 | } 46 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/home/HomeScreen.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.home 2 | 3 | import androidx.compose.foundation.layout.* 4 | import androidx.compose.foundation.shape.RoundedCornerShape 5 | import androidx.compose.material.Card 6 | import androidx.compose.material.Icon 7 | import androidx.compose.material.MaterialTheme 8 | import androidx.compose.material.Text 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.graphics.vector.ImageVector 13 | import androidx.compose.ui.text.style.TextAlign 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.unit.sp 16 | import compose.icons.FontAwesomeIcons 17 | import compose.icons.fontawesomeicons.Solid 18 | import compose.icons.fontawesomeicons.solid.CashRegister 19 | import compose.icons.fontawesomeicons.solid.PaperPlane 20 | import compose.icons.fontawesomeicons.solid.Paperclip 21 | import compose.icons.fontawesomeicons.solid.Users 22 | import ixidev.jetspring.ui.navigation.navcontroller.NavController 23 | 24 | /*** 25 | * Created by ixiDev on 18/08/2022 26 | * GitHub : https://www.github.com/ixiDev 27 | **/ 28 | 29 | @Composable 30 | fun HomeScreen(viewModel: HomeViewModel, navController: NavController) { 31 | 32 | Row(modifier = Modifier.fillMaxWidth()) { 33 | CategoryView( 34 | modifier = Modifier.weight(1f), 35 | label = "الفواتير", 36 | icon = FontAwesomeIcons.Solid.Paperclip 37 | ) 38 | CategoryView( 39 | modifier = Modifier.weight(1f), 40 | label = "الإشتراكات", 41 | icon = FontAwesomeIcons.Solid.PaperPlane 42 | ) 43 | CategoryView( 44 | modifier = Modifier.weight(1f), 45 | label = "المصروفات", 46 | icon = FontAwesomeIcons.Solid.CashRegister 47 | ) 48 | 49 | CategoryView( 50 | modifier = Modifier.weight(1f), 51 | label = "الموضفين", 52 | icon = FontAwesomeIcons.Solid.Users 53 | ) 54 | 55 | } 56 | } 57 | 58 | 59 | @Composable 60 | fun CategoryView( 61 | modifier: Modifier = Modifier, 62 | label: String, 63 | icon: ImageVector 64 | ) { 65 | Card( 66 | modifier = modifier 67 | .padding(10.dp), 68 | backgroundColor = MaterialTheme.colors.primary, 69 | shape = RoundedCornerShape(10.dp), 70 | ) { 71 | 72 | Column( 73 | modifier = Modifier.fillMaxWidth() 74 | .height(100.dp), 75 | verticalArrangement = Arrangement.Center, 76 | horizontalAlignment = Alignment.CenterHorizontally 77 | ) { 78 | Icon( 79 | imageVector = icon, 80 | contentDescription = null, 81 | modifier = Modifier.size(40.dp) 82 | ) 83 | Spacer(modifier = Modifier.height(10.dp)) 84 | Text( 85 | text = label, 86 | textAlign = TextAlign.Center, 87 | fontSize = 20.sp 88 | ) 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/home/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.home 2 | 3 | import ixidev.jetspring.base.BaseViewModel 4 | import ixidev.jetspring.data.DataService 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import org.springframework.stereotype.Component 7 | 8 | /*** 9 | * Created by ixiDev on 19/08/2022 10 | * GitHub : https://www.github.com/ixiDev 11 | **/ 12 | 13 | @Component 14 | class HomeViewModel constructor( 15 | private val dataService: DataService 16 | ) : BaseViewModel() { 17 | 18 | val clientsCount: MutableStateFlow = MutableStateFlow(0) 19 | 20 | fun getCount() { 21 | launchOnIO { 22 | clientsCount.emit( 23 | dataService.getCount() 24 | ) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/navigation/Screens.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.navigation 2 | 3 | import androidx.compose.material.icons.Icons 4 | import androidx.compose.material.icons.rounded.Home 5 | import androidx.compose.material.icons.rounded.Settings 6 | import androidx.compose.ui.graphics.vector.ImageVector 7 | import compose.icons.FontAwesomeIcons 8 | import compose.icons.fontawesomeicons.Solid 9 | import compose.icons.fontawesomeicons.solid.ExternalLinkAlt 10 | 11 | /*** 12 | * Created by ixiDev on 19/08/2022 13 | * GitHub : https://www.github.com/ixiDev 14 | **/ 15 | 16 | 17 | sealed class Screens( 18 | val label: String, 19 | val icon: ImageVector? = null 20 | ) { 21 | object Home : Screens( 22 | label = "global.label.home", 23 | icon = Icons.Rounded.Home 24 | ) 25 | 26 | object Settings : Screens( 27 | label = "global.label.settings", 28 | icon = Icons.Rounded.Settings 29 | ) 30 | 31 | object NewWindow : Screens( 32 | label = "global.label.newWindow", 33 | icon = FontAwesomeIcons.Solid.ExternalLinkAlt 34 | ) 35 | 36 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/navigation/navcontroller/NavController.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.navigation.navcontroller 2 | 3 | 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.MutableState 6 | import androidx.compose.runtime.mutableStateOf 7 | import androidx.compose.runtime.saveable.rememberSaveable 8 | import ixidev.jetspring.ui.navigation.Screens 9 | import org.springframework.context.ApplicationContext 10 | 11 | /*** 12 | * Created by ixiDev on 19/08/2022 13 | * GitHub : https://www.github.com/ixiDev 14 | **/ 15 | class NavController( 16 | val context: ApplicationContext, 17 | private val startDestination: Screens, 18 | private var backStackScreens: MutableSet = mutableSetOf(), 19 | private var backStackWindows: MutableSet = mutableSetOf() 20 | ) { 21 | // Variable to store the state of the current screen 22 | var currentScreen: MutableState = mutableStateOf(startDestination) 23 | var currentWindow: MutableState = mutableStateOf(null) 24 | 25 | private val routes = mutableSetOf() 26 | 27 | // Function to handle the navigation between the screen 28 | fun navigate(route: Screens) { 29 | if (route !in routes) { 30 | throw RuntimeException("Route '$route' not exist") 31 | } 32 | if (route != currentScreen.value) { 33 | if (backStackScreens.contains(currentScreen.value) && currentScreen.value != startDestination) { 34 | backStackScreens.remove(currentScreen.value) 35 | } 36 | 37 | if (route == startDestination) { 38 | backStackScreens = mutableSetOf() 39 | } else { 40 | backStackScreens.add(currentScreen.value) 41 | } 42 | 43 | currentScreen.value = route 44 | } 45 | } 46 | 47 | fun window(route: Screens) { 48 | if (route !in routes) { 49 | throw RuntimeException("Route '$route' not exist") 50 | } 51 | if (route != currentWindow.value) { 52 | if (backStackWindows.contains(currentWindow.value) && currentWindow.value != null) { 53 | backStackWindows.remove(currentWindow.value) 54 | } 55 | currentWindow.value = route 56 | } 57 | } 58 | 59 | fun addRoute(route: Screens) { 60 | routes.add(route) 61 | } 62 | 63 | // Function to handle the back 64 | fun navigateBack() { 65 | if (backStackScreens.isNotEmpty()) { 66 | currentScreen.value = backStackScreens.last() 67 | backStackScreens.remove(currentScreen.value) 68 | } 69 | 70 | } 71 | 72 | fun popBackWindow() { 73 | currentWindow.value = null 74 | } 75 | } 76 | 77 | 78 | /** 79 | * Composable to remember the state of the navcontroller 80 | */ 81 | @Composable 82 | fun rememberNavController( 83 | context: ApplicationContext, 84 | startDestination: Screens, 85 | backStackScreens: MutableSet = mutableSetOf() 86 | ): MutableState = rememberSaveable { 87 | mutableStateOf(NavController(context, startDestination, backStackScreens)) 88 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/navigation/navcontroller/NavigationHost.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.navigation.navcontroller 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.Surface 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.CompositionLocalProvider 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.platform.LocalLayoutDirection 11 | import androidx.compose.ui.unit.Dp 12 | import androidx.compose.ui.unit.LayoutDirection 13 | import androidx.compose.ui.window.Window 14 | import androidx.compose.ui.window.WindowPosition 15 | import androidx.compose.ui.window.rememberWindowState 16 | import ixidev.jetspring.base.BaseViewModel 17 | import ixidev.jetspring.ui.navigation.Screens 18 | 19 | /*** 20 | * Created by ixiDev on 19/08/2022 21 | * GitHub : https://www.github.com/ixiDev 22 | **/ 23 | class NavigationHost( 24 | val navController: NavController, 25 | val windowScreens: (@Composable NavigationGraphBuilder.() -> Unit)? = null, 26 | val mainScreens: @Composable NavigationGraphBuilder.() -> Unit 27 | ) { 28 | 29 | @Composable 30 | fun build() { 31 | NavigationGraphBuilder().renderContents() 32 | } 33 | 34 | inner class NavigationGraphBuilder( 35 | val navController: NavController = this@NavigationHost.navController 36 | ) { 37 | @Composable 38 | fun renderContents() { 39 | this@NavigationHost.mainScreens(this) 40 | this@NavigationHost.windowScreens?.invoke(this) 41 | } 42 | } 43 | } 44 | 45 | 46 | /** 47 | * Composable to build the Navigation Host 48 | */ 49 | @Composable 50 | fun NavigationHost.NavigationGraphBuilder.composable( 51 | route: Screens, 52 | content: @Composable () -> Unit 53 | ) { 54 | navController.addRoute(route) 55 | if (navController.currentScreen.value == route) { 56 | content() 57 | } 58 | } 59 | 60 | @Composable 61 | fun NavigationHost.NavigationGraphBuilder.windowComposable( 62 | route: Screens, 63 | content: @Composable () -> Unit 64 | ) { 65 | navController.addRoute(route) 66 | if (navController.currentWindow.value == route) { 67 | Window( 68 | onCloseRequest = { 69 | navController.currentWindow.value = null 70 | }, 71 | title = route.label, 72 | state = rememberWindowState( 73 | height = Dp.Unspecified, 74 | position = WindowPosition(Alignment.Center) 75 | ), 76 | ) { 77 | CompositionLocalProvider( 78 | LocalLayoutDirection provides LayoutDirection.Rtl 79 | ) { 80 | Surface( 81 | modifier = Modifier.background(color = MaterialTheme.colors.background) 82 | ) { 83 | content() 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | @Composable 91 | inline fun NavigationHost.NavigationGraphBuilder.viewModelComposable( 92 | route: Screens, 93 | crossinline content: @Composable VM.(NavController) -> Unit 94 | ) where VM : BaseViewModel { 95 | 96 | composable(route) { 97 | content( 98 | navController.context.getBean(VM::class.java), 99 | navController 100 | ) 101 | } 102 | } 103 | 104 | @Composable 105 | inline fun NavigationHost.NavigationGraphBuilder.viewModelWindowComposable( 106 | route: Screens, 107 | crossinline content: @Composable VM.(NavController) -> Unit 108 | ) where VM : BaseViewModel { 109 | 110 | windowComposable(route) { 111 | content( 112 | navController.context.getBean(VM::class.java), 113 | navController 114 | ) 115 | } 116 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/theme/AppThemeState.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.theme 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.MutableState 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.remember 7 | 8 | /** 9 | ** Project : JetIPTV 10 | ** Created By : Abdelmajid ID ALI 11 | ** On : 26/04/2022 12 | ** Email : feedback.mrzero@gmail.com 13 | **/ 14 | class AppThemeState { 15 | private val themeState: MutableState = mutableStateOf(false) 16 | var isDarkTheme: Boolean = false 17 | get() = themeState.value 18 | set(value) { 19 | field = value 20 | themeState.value = field 21 | } 22 | } 23 | 24 | @Composable 25 | fun rememberAppThemeState(): AppThemeState = remember { 26 | AppThemeState() 27 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFF9FA8DA) 6 | val Purple500 = Color(0xFF2196F3) 7 | val Purple700 = Color(0xFF1976D2) 8 | val Teal200 = Color(0xFF03DAC5) -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun JetSpringAppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { 32 | val colors = if (darkTheme) { 33 | DarkColorPalette 34 | } else { 35 | LightColorPalette 36 | } 37 | MaterialTheme( 38 | colors = colors, 39 | typography = Typography, 40 | shapes = Shapes, 41 | content = content 42 | ) 43 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.ui.theme 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontStyle 7 | import androidx.compose.ui.text.font.FontWeight 8 | import androidx.compose.ui.text.platform.Font 9 | import androidx.compose.ui.unit.sp 10 | 11 | // Set of Material typography styles to start with 12 | 13 | val fontFamily = FontFamily( 14 | Font( 15 | resource = "Almarai-Regular.ttf", 16 | weight = FontWeight.W400, 17 | style = FontStyle.Normal 18 | ) 19 | ) 20 | val Typography = Typography( 21 | defaultFontFamily = fontFamily, 22 | body1 = TextStyle( 23 | fontFamily = fontFamily, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 18.sp 26 | ) 27 | ) -------------------------------------------------------------------------------- /composeApp/src/commonMain/kotlin/ixidev/jetspring/utils/ExtHelpers.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring.utils 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.runtime.staticCompositionLocalOf 5 | import ixidev.jetspring.base.BaseViewModel 6 | import org.springframework.context.ApplicationContext 7 | import java.time.LocalDate 8 | import java.time.ZoneId 9 | import java.util.* 10 | import java.util.logging.Logger 11 | 12 | 13 | /*** 14 | * Created by ixiDev on 20/08/2022 15 | * GitHub : https://www.github.com/ixiDev 16 | **/ 17 | private fun ResourceBundle.valueOf(code: String): String { 18 | return if (containsKey(code)) { 19 | getString(code) 20 | } else { 21 | code 22 | } 23 | } 24 | 25 | 26 | inline fun ApplicationContext.viewModel(): T { 27 | return getBean(T::class.java) 28 | } 29 | 30 | val LocalApplicationContext = staticCompositionLocalOf { 31 | error("ApplicationContext not provided") 32 | } 33 | 34 | val LocalResourceBundle = staticCompositionLocalOf { 35 | error("ResourceBundle not provided") 36 | } 37 | 38 | val LocalLogger = staticCompositionLocalOf { 39 | error("ResourceBundle not provided") 40 | } 41 | 42 | @Composable 43 | fun stringResource(key: String): String { 44 | return LocalResourceBundle.current.valueOf(key) 45 | } 46 | 47 | fun String.isNumeric() = all { it in '0'..'9' } 48 | 49 | 50 | fun LocalDate.toDate(): Date { 51 | return Date.from(atStartOfDay(ZoneId.systemDefault()).toInstant()) 52 | } -------------------------------------------------------------------------------- /composeApp/src/commonMain/resources/Almarai-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixiDev/JetSpring/de6d510cde5b27f57d84dffc43ffb6e9f41bcd99/composeApp/src/commonMain/resources/Almarai-Regular.ttf -------------------------------------------------------------------------------- /composeApp/src/commonMain/resources/app-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.main.web-application-type=none 2 | spring.datasource.url=jdbc:mysql://localhost:3306/jet_spring 3 | spring.datasource.username=root 4 | spring.datasource.password= 5 | spring.jpa.hibernate.ddl-auto=update 6 | #spring.jpa.hibernate.enable_lazy_load_no_tra=true 7 | spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true 8 | logging.file.name=jetSpring.log 9 | logging.file.path=./ 10 | spring.messages.encoding=UTF-8 11 | -------------------------------------------------------------------------------- /composeApp/src/commonMain/resources/strings.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "UnusedProperty" for whole file 2 | global.label.home=Home 3 | global.login=Login 4 | global.password=Password 5 | global.username=Username 6 | global.app.name=JetSpring 7 | global.label.newWindow=New Window 8 | global.label.settings=Settings 9 | global.login.error=Username or password incorrect -------------------------------------------------------------------------------- /composeApp/src/commonMain/resources/strings_ar.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "UnusedProperty" for whole file 2 | # suppress inspection "InconsistentResourceBundle" for whole file 3 | global.label.home=\u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629 4 | global.login=\u062A\u0633\u062C\u064A\u0644 \u0627\u0644\u062F\u062E\u0648\u0644 5 | global.password=\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631 6 | global.username=\u0627\u0633\u0645 \u0627\u0644\u0645\u0633\u062A\u062E\u062F\u0645 7 | global.login.error=\u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u063A\u064A\u0631 \u0635\u062D\u064A\u062D\u0629 8 | global.app.name=\u062C\u0645\u0639\u064A\u0629 \u062A\u0645\u0644\u0627\u0644\u062A 9 | global.label.settings=\u0625\u0639\u062F\u0627\u062F\u0627\u062A \u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C 10 | global.label.newWindow=\u0646\u0627\u0641\u062F\u0629 \u062C\u062F\u064A\u062F\u0629 -------------------------------------------------------------------------------- /composeApp/src/desktopMain/kotlin/ixidev/jetspring/Main.kt: -------------------------------------------------------------------------------- 1 | package ixidev.jetspring 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.* 5 | import androidx.compose.material.MaterialTheme 6 | import androidx.compose.material.Surface 7 | import androidx.compose.material.Text 8 | import androidx.compose.runtime.* 9 | import androidx.compose.ui.Alignment 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.platform.LocalLayoutDirection 12 | import androidx.compose.ui.res.painterResource 13 | import androidx.compose.ui.unit.LayoutDirection 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.window.* 16 | import ixidev.jetspring.security.UserAuthSate 17 | import ixidev.jetspring.ui.auth.LoginScreen 18 | import ixidev.jetspring.ui.components.AppWindowTitleBar 19 | import ixidev.jetspring.ui.components.SideBarMenu 20 | import ixidev.jetspring.ui.home.HomeScreen 21 | import ixidev.jetspring.ui.home.HomeViewModel 22 | import ixidev.jetspring.ui.navigation.Screens 23 | import ixidev.jetspring.ui.navigation.navcontroller.* 24 | import ixidev.jetspring.ui.theme.AppThemeState 25 | import ixidev.jetspring.ui.theme.JetSpringAppTheme 26 | import ixidev.jetspring.ui.theme.rememberAppThemeState 27 | import ixidev.jetspring.utils.* 28 | import org.springframework.boot.SpringApplication 29 | import org.springframework.boot.autoconfigure.AutoConfiguration 30 | import org.springframework.boot.autoconfigure.SpringBootApplication 31 | import org.springframework.boot.runApplication 32 | import org.springframework.context.ApplicationContext 33 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing 34 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories 35 | import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication 36 | import org.springframework.security.core.context.SecurityContextHolder 37 | import java.util.* 38 | import java.util.logging.Logger 39 | 40 | 41 | @SpringBootApplication 42 | @EnableJpaRepositories 43 | @EnableJpaAuditing 44 | @AutoConfiguration 45 | @EnableGlobalAuthentication 46 | class Main { 47 | init { 48 | System.setProperty( 49 | SecurityContextHolder.SYSTEM_PROPERTY, 50 | SecurityContextHolder.MODE_GLOBAL 51 | ) 52 | } 53 | } 54 | 55 | fun main(args: Array) = application { 56 | val logger = Logger.getLogger(Main::class.simpleName) 57 | val applicationContext = runApplication
(*args) 58 | val resourceBundle = ResourceBundle.getBundle( 59 | "strings", 60 | Locale.forLanguageTag("en") // or Locale.forLanguageTag("ar") 61 | ) 62 | val authSate = remember { applicationContext.getBean(UserAuthSate::class.java) } 63 | val auth by authSate.auth.collectAsState(false) 64 | val themeState = rememberAppThemeState() 65 | CompositionLocalProvider( 66 | LocalApplicationContext provides applicationContext, 67 | LocalResourceBundle provides resourceBundle, 68 | LocalLogger provides logger 69 | ) { 70 | JetSpringAppTheme(darkTheme = themeState.isDarkTheme) { 71 | if (auth) { 72 | AppMainWindow(LocalApplicationContext.current, themeState) 73 | } else { 74 | AppLoginWindow(LocalApplicationContext.current) 75 | } 76 | } 77 | } 78 | 79 | } 80 | 81 | @Composable 82 | private fun ApplicationScope.AppLoginWindow(applicationContext: ApplicationContext) { 83 | val loginWindowState = rememberWindowState( 84 | position = WindowPosition(Alignment.Center), 85 | width = 400.dp, 86 | height = 600.dp, 87 | ) 88 | Window( 89 | onCloseRequest = { 90 | SpringApplication.exit(applicationContext) 91 | exitApplication() 92 | }, 93 | state = loginWindowState, 94 | resizable = false, 95 | title = stringResource("global.login"), 96 | icon = painterResource("app-icon.svg") 97 | ) { 98 | CompositionLocalProvider( 99 | LocalLayoutDirection provides LayoutDirection.Ltr 100 | ) { 101 | LoginScreen(applicationContext.viewModel()) 102 | } 103 | } 104 | } 105 | 106 | @Composable 107 | private fun ApplicationScope.AppMainWindow( 108 | applicationContext: ApplicationContext, 109 | themeState: AppThemeState 110 | ) { 111 | val globalWindowState = rememberWindowState( 112 | position = WindowPosition(Alignment.Center), 113 | width = 1400.dp, 114 | height = 800.dp, 115 | ) 116 | Window( 117 | onCloseRequest = { 118 | SpringApplication.exit(applicationContext) 119 | exitApplication() 120 | }, 121 | state = globalWindowState, 122 | undecorated = true, 123 | icon = painterResource("app-icon.svg") 124 | ) { 125 | CompositionLocalProvider( 126 | LocalLayoutDirection provides LayoutDirection.Ltr 127 | ) { 128 | Surface( 129 | modifier = Modifier.background(color = MaterialTheme.colors.background) 130 | ) { 131 | Column(modifier = Modifier.fillMaxSize()) { 132 | AppWindowTitleBar( 133 | applicationContext.viewModel(), 134 | globalWindowState, 135 | themeState 136 | ) { 137 | exitApplication() 138 | } 139 | AppMainContainer(applicationContext) 140 | } 141 | } 142 | } 143 | } 144 | } 145 | 146 | 147 | @Composable 148 | fun AppMainContainer(context: ApplicationContext) { 149 | val navController by rememberNavController(context, Screens.Home) 150 | Row( 151 | modifier = Modifier.fillMaxSize() 152 | ) { 153 | SideBarMenu( 154 | modifier = Modifier 155 | .weight(0.15f), 156 | navController 157 | ) 158 | Box( 159 | modifier = Modifier.fillMaxHeight() 160 | .weight(0.85f) 161 | ) { 162 | AppNavigationHost(navController = navController) 163 | } 164 | } 165 | } 166 | 167 | 168 | @Composable 169 | fun AppNavigationHost(navController: NavController) { 170 | NavigationHost( 171 | navController, 172 | windowScreens = { 173 | // add window navigation here 174 | //viewModelWindowComposable<>() 175 | windowComposable(Screens.NewWindow) { 176 | Text("New Window") 177 | } 178 | } 179 | ) { 180 | viewModelComposable(Screens.Home) { 181 | HomeScreen(this, navController) 182 | } 183 | composable(Screens.Settings) { 184 | Text("Settings") 185 | } 186 | }.build() 187 | } 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | 3 | #Gradle 4 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | compose-plugin = "1.6.10" 3 | junit = "4.13.2" 4 | kotlin = "2.0.0" 5 | 6 | [libraries] 7 | kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } 8 | kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } 9 | junit = { group = "junit", name = "junit", version.ref = "junit" } 10 | 11 | 12 | [plugins] 13 | jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } 14 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 15 | kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ixiDev/JetSpring/de6d510cde5b27f57d84dffc43ffb6e9f41bcd99/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "JetSpring" 2 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 3 | 4 | pluginManagement { 5 | repositories { 6 | google { 7 | mavenContent { 8 | includeGroupAndSubgroups("androidx") 9 | includeGroupAndSubgroups("com.android") 10 | includeGroupAndSubgroups("com.google") 11 | } 12 | } 13 | mavenCentral() 14 | gradlePluginPortal() 15 | } 16 | } 17 | 18 | dependencyResolutionManagement { 19 | repositories { 20 | google { 21 | mavenContent { 22 | includeGroupAndSubgroups("androidx") 23 | includeGroupAndSubgroups("com.android") 24 | includeGroupAndSubgroups("com.google") 25 | } 26 | } 27 | mavenCentral() 28 | } 29 | } 30 | 31 | include(":composeApp") --------------------------------------------------------------------------------