new styles

This commit is contained in:
User
2025-01-06 20:19:07 +03:00
parent 8b589470ac
commit 847102e843
51 changed files with 8457 additions and 627 deletions

View File

@@ -10,6 +10,11 @@
"dependencies": {
"@clerk/clerk-react": "^5.21.0",
"@coreui/react": "^5.4.1",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-navigation-menu": "^1.2.3",
"@radix-ui/react-slider": "^1.2.2",
"lucide-react": "^0.469.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.1.1"
@@ -1104,6 +1109,481 @@
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@radix-ui/number": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz",
"integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==",
"license": "MIT"
},
"node_modules/@radix-ui/primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz",
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-accordion": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.2.tgz",
"integrity": "sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-collapsible": "1.1.2",
"@radix-ui/react-collection": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-controllable-state": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-checkbox": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.3.tgz",
"integrity": "sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-previous": "1.1.0",
"@radix-ui/react-use-size": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz",
"integrity": "sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz",
"integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-slot": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-context": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-direction": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
"integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz",
"integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-escape-keydown": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-id": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-navigation-menu": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.3.tgz",
"integrity": "sha512-IQWAsQ7dsLIYDrn0WqPU+cdM7MONTv9nqrLVYoie3BPiabSfUVDe6Fr+oEt0Cofsr9ONDcDe9xhmJbL1Uq1yKg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-collection": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-dismissable-layer": "1.1.3",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-callback-ref": "1.1.0",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-use-previous": "1.1.0",
"@radix-ui/react-visually-hidden": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-presence": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz",
"integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-primitive": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz",
"integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slider": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.2.2.tgz",
"integrity": "sha512-sNlU06ii1/ZcbHf8I9En54ZPW0Vil/yPVg4vQMcFNjrIx51jsHbFl1HYHQvCIWJSr1q0ZmA+iIs/ZTv8h7HHSA==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.0",
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-collection": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-primitive": "2.0.1",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-layout-effect": "1.1.0",
"@radix-ui/react-use-previous": "1.1.0",
"@radix-ui/react-use-size": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz",
"integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-callback-ref": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-escape-keydown": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-callback-ref": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-previous": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz",
"integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-size": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-visually-hidden": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz",
"integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz",
@@ -1439,13 +1919,13 @@
"version": "15.7.11",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
"dev": true
"devOptional": true
},
"node_modules/@types/react": {
"version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -1456,7 +1936,7 @@
"version": "18.3.5",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz",
"integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
"dev": true,
"devOptional": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^18.0.0"
@@ -1900,7 +2380,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
"devOptional": true
},
"node_modules/data-view-buffer": {
"version": "1.0.2",
@@ -3476,6 +3956,15 @@
"yallist": "^3.0.2"
}
},
"node_modules/lucide-react": {
"version": "0.469.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.469.0.tgz",
"integrity": "sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",

View File

@@ -12,6 +12,11 @@
"dependencies": {
"@clerk/clerk-react": "^5.21.0",
"@coreui/react": "^5.4.1",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-navigation-menu": "^1.2.3",
"@radix-ui/react-slider": "^1.2.2",
"lucide-react": "^0.469.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.1.1"

View File

@@ -10,7 +10,7 @@ body {
}
.header {
padding: 20px;
padding: 30px;
background-color: #6c3483;
}

View File

@@ -1,19 +1,11 @@
import React from 'react';
import Navbar from './components/Navbar';
import Sidebar from './components/Sidebar';
import Home from './Home';
import './App.css';
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";
const App = () => {
return (
<div className="app">
<Navbar />
<div className="main-content">
<Sidebar />
<Home />
</div>
</div>
);
};
export default App;
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);

View File

@@ -1,47 +0,0 @@
/* Стили для Header */
.header {
background-color: #da1eff; /* Голубой цвет фона */
color: #ffffff; /* Белый цвет текста */
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 100px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.logo {
font-size: 36px;
font-weight: bold;
}
.project-name {
margin: 0;
font-family: 'Calibri', sans-serif;
}
.nav {
font-family: 'Calibri', sans-serif;
}
.nav-links {
display: flex;
list-style-type: none;
gap: 20px;
}
.nav-links li {
margin: 0;
}
.nav-links a {
color: #ffffff;
text-decoration: none;
font-size: 18px;
transition: color 0.3s;
}
.nav-links a:hover {
color: #f0f8ff; /* Светлый голубой цвет при наведении */
}

View File

@@ -1,15 +0,0 @@
import React from "react";
import "./Header.css"; // Подключаем стили для Header
const Header = () => {
return (
<header className="header">
<div className="logo">
<h4 className="project-name">Eternos</h4>
</div>
</header>
);
};
export default Header;

View File

@@ -1,16 +0,0 @@
.home {
padding: 2rem;
}
.home-title {
font-size: 2rem;
margin-bottom: 2rem;
text-align: center;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}

View File

@@ -1,23 +0,0 @@
import React from "react";
import ProductCard from "./components/ProductCard";
const Home = () => {
const products = [
{ id: 1, name: "Product 1", price: 50, image: "https://via.placeholder.com/150" },
{ id: 2, name: "Product 2", price: 75, image: "https://via.placeholder.com/150" },
{ id: 3, name: "Product 3", price: 100, image: "https://via.placeholder.com/150" },
];
return (
<div className="home">
<h1 className="home-title">Welcome to Eternos</h1>
<div className="product-grid">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
</div>
);
};
export default Home;

View File

@@ -1,141 +0,0 @@
.navbar {
font-family: Arial, sans-serif;
background-color: #f8f8f8;
border-bottom: 1px solid #ddd;
}
.navbar-main {
display: flex;
align-items: center;
padding: 10px 20px;
}
.logo img {
height: 30px;
}
.catalog-button {
background-color: #0073e6;
color: white;
border: none;
padding: 5px 10px;
margin-left: 20px;
cursor: pointer;
border-radius: 5px;
}
.search-bar {
display: flex;
flex-grow: 1;
margin-left: 20px;
}
.search-bar select,
.search-bar input {
border-radius: 5px; padding: 5px;
border: 1px solid #ddd;
}
.search-bar input {
border-radius: 5px;
flex-grow: 1;
margin-left: 5px;
}
.search-button {
border-radius: 5px;
background-color: #0073e6;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
}
.personal-doc-button {
background-color: #0073e6;
color: white;
border: none;
padding: 5px 15px;
margin-left: 20px;
border-radius: 5px;
cursor: pointer;
}
.navbar-bottom {
display: flex;
justify-content: space-around;
padding: 10px;
font-size: 14px;
background-color: #fff;
}
.navbar-bottom a {
text-decoration: none;
color: #0073e6;
}
/* Адаптация для мобильных устройств */
@media (max-width: 768px) {
.navbar-main {
flex-direction: column;
align-items: flex-start;
padding: 10px;
}
.logo {
margin-bottom: 10px;
}
.catalog-button {
width: 100%;
text-align: center;
margin: 0;
margin-bottom: 10px;
}
.search-bar {
width: 100%;
flex-direction: column;
}
.search-bar select,
.search-bar input,
.search-button {
width: 100%;
margin: 5px 0;
}
.personal-doc-button {
width: 100%;
margin: 10px 0;
}
.navbar-bottom {
flex-wrap: wrap;
justify-content: flex-start;
}
.navbar-bottom a {
margin: 5px;
font-size: 12px;
}
}
/* Адаптация для очень маленьких экранов (например, телефоны) */
@media (max-width: 480px) {
.navbar-main {
align-items: center;
}
.search-bar select {
display: none; /* Убираем селектор "Везде" для упрощения */
}
.navbar-bottom a {
flex: 1 1 100%; /* Каждая ссылка занимает всю ширину */
text-align: center;
}
}

View File

@@ -1,57 +0,0 @@
import React, { useState } from "react";
import "./Navbar.css";
import Sidebar from "./Sidebar";
const Navbar = () => {
const [isSidebarVisible, setIsSidebarVisible] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const toggleSidebar = () => {
// Открывать Sidebar только если пользователь ввел текст
if (searchQuery.trim() !== "") {
setIsSidebarVisible(!isSidebarVisible);
} else {
alert("Пожалуйста, введите текст для поиска.");
}
};
const handleSearchChange = (e) => {
setSearchQuery(e.target.value);
};
return (
<div>
<nav className="navbar">
<div className="navbar-main">
<div className="logo">
<img src="" alt="Eternos" />
</div>
<button className="catalog-button" onClick={toggleSidebar}>
Каталог
</button>
<div className="search-bar">
<select>
<option value="all">Везде</option>
</select>
<input
type="text"
placeholder="Искать на Eternos"
value={searchQuery}
onChange={handleSearchChange}
/>
<button className="search-button">🔍</button>
</div>
<button className="personal-doc-button">Личный кабинет</button>
</div>
</nav>
{isSidebarVisible && (
<div className="sidebar-overlay">
<Sidebar />
</div>
)}
</div>
);
};
export default Navbar;

View File

@@ -1,33 +0,0 @@
.product-card {
border: 1px solid #d1d5db;
border-radius: 10px;
padding: 1rem;
text-align: center;
background-color: #ffffff;
}
.product-image {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 5px;
}
.product-title {
font-size: 1.2rem;
margin: 1rem 0;
}
.product-price {
font-size: 1rem;
color: #4ade80;
}
.add-to-cart-btn {
background-color: #1e3a8a;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 5px;
cursor: pointer;
}

View File

@@ -1,15 +0,0 @@
import React from "react";
import "./ProductCard.css";
const ProductCard = ({ product }) => {
return (
<div className="product-card">
<img src={product.image} alt={product.name} className="product-image" />
<h3 className="product-title">{product.name}</h3>
<p className="product-price">${product.price}</p>
<button className="add-to-cart-btn">Add to Cart</button>
</div>
);
};
export default ProductCard;

View File

@@ -1,82 +0,0 @@
.sidebar {
width: 300px;
background-color: #ffffff;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 0;
font-family: Arial, sans-serif;
}
.sidebar h2 {
font-size: 1.5rem;
margin-bottom: 20px;
}
.filter-section {
margin-bottom: 20px;
}
.filter-section h3 {
font-size: 1.2rem;
margin-bottom: 10px;
}
.price-inputs input {
width: 45%;
padding: 5px;
margin-right: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.price-categories label,
.filter-section label {
display: block;
margin-bottom: 5px;
font-size: 1rem;
}
label.switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
}
label.switch input {
opacity: 0;
width: 0;
height: 0;
}
label.switch .slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: 34px;
transition: 0.4s;
}
label.switch .slider:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background-color: white;
border-radius: 50%;
transition: 0.4s;
}
label.switch input:checked + .slider {
background-color: #0073e6;
}
label.switch input:checked + .slider:before {
transform: translateX(20px);
}

View File

@@ -1,100 +0,0 @@
import React, { useState } from "react";
import "./Sidebar.css";
const Sidebar = () => {
const [priceRange, setPriceRange] = useState({ min: 0, max: 25000 });
const [selectedPriceCategory, setSelectedPriceCategory] = useState("any");
const [isOriginal, setIsOriginal] = useState(false);
const [teaSort, setTeaSort] = useState("");
const handlePriceRangeChange = (e) => {
const { name, value } = e.target;
setPriceRange((prev) => ({
...prev,
[name]: value,
}));
};
return (
<div className="sidebar">
<h2>Фильтры</h2>
<div className="filter-section">
<h3>Рассрочка 0-0-6</h3>
<label className="switch">
<input
type="checkbox"
checked={isOriginal}
onChange={() => setIsOriginal(!isOriginal)}
/>
<span className="slider"></span>
</label>
</div>
<div className="filter-section">
<h3>Цена</h3>
<div className="price-inputs">
<input
type="number"
name="min"
value={priceRange.min}
onChange={handlePriceRangeChange}
placeholder="Минимум"
/>
<input
type="number"
name="max"
value={priceRange.max}
onChange={handlePriceRangeChange}
placeholder="Максимум"
/>
</div>
<div className="price-categories">
{["до 500 ₽", "5001 000 ₽", "1 0002 000 ₽", "2 000 ₽ и дороже", "Неважно"].map(
(category, index) => (
<label key={index}>
<input
type="radio"
name="price-category"
value={category}
checked={selectedPriceCategory === category}
onChange={() => setSelectedPriceCategory(category)}
/>
{category}
</label>
)
)}
</div>
</div>
<div className="filter-section">
<h3>Оригинальный товар</h3>
<label className="switch">
<input
type="checkbox"
checked={isOriginal}
onChange={() => setIsOriginal(!isOriginal)}
/>
<span className="slider"></span>
</label>
</div>
<div className="filter-section">
<h3>Сорт чая</h3>
{["Матча", "Сенча", "Улун", "Черный чай"].map((sort) => (
<label key={sort}>
<input
type="checkbox"
value={sort}
checked={teaSort === sort}
onChange={() => setTeaSort(sort)}
/>
{sort}
</label>
))}
</div>
</div>
);
};
export default Sidebar;

View File

@@ -1,70 +1,13 @@
body {
margin: 0;
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #8e44ad, #ffffff);
color: #333;
}
.products-page {
padding: 20px;
text-align: center;
}
.page-title {
font-size: 2.5rem;
color: #6c3483;
margin-bottom: 20px;
}
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
padding: 0 20px;
}
.product-card {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 20px;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
}
.product-image {
max-width: 100%;
border-radius: 8px;
}
.product-name {
font-size: 1.5rem;
color: #6c3483;
margin: 10px 0;
}
.product-price {
font-size: 1.2rem;
color: #8e44ad;
margin: 5px 0;
}
.product-button {
background-color: #8e44ad;
color: #ffffff;
border: none;
padding: 10px 20px;
border-radius: 5px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.2s;
}
.product-button:hover {
background-color: #6c3483;
}
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@@ -1,11 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./index.css";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
document.getElementById("root")
);

View File

@@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}

36
frontend/style/.gitignore vendored Normal file
View File

@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

36
frontend/style/README.md Normal file
View File

@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@@ -0,0 +1,24 @@
import { CartItems } from "@/components/cart-items"
import { CartSummary } from "@/components/cart-summary"
export default function CartPage() {
return (
<div className="container mx-auto px-4 py-8">
<div className="flex gap-8">
<div className="flex-1">
<div className="flex items-center justify-between mb-6">
<h1 className="text-2xl font-bold">Корзина</h1>
<button className="text-blue-600 hover:underline">
Поделиться
</button>
</div>
<CartItems />
</div>
<div className="w-80">
<CartSummary />
</div>
</div>
</div>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,78 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
font-family: Arial, Helvetica, sans-serif;
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View File

@@ -0,0 +1,35 @@
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

View File

@@ -0,0 +1,28 @@
import { ProductGrid } from "@/components/product-grid"
import { Header } from "@/components/header"
import { Banner } from "@/components/banner"
import { ProductFilters } from "@/components/product-filters"
import { Footer } from "@/components/footer"
export default function HomePage() {
return (
<>
<div className="container mx-auto px-4 py-8">
<Header/>
<Banner />
<div className="my-8 flex gap-8">
<ProductFilters />
<div className="flex-1">
<h2 className="text-2xl font-bold mb-6">Специальные предложения!</h2>
<ProductGrid />
</div>
</div>
</div>
<Footer />
</>
)
}

View File

@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@@ -0,0 +1,29 @@
import Image from "next/image"
import { Button } from "./ui/button"
export function Banner() {
return (
<div className="relative w-full h-[300px] rounded-lg overflow-hidden">
<Image
src="/placeholder.svg"
alt="Promotional Banner"
fill
className="object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-r from-blue-600/90 to-blue-600/50 flex items-center">
<div className="container mx-auto px-4">
<div className="max-w-lg text-white">
<h1 className="text-4xl font-bold mb-4">САМОЕ ЗАВЕТНОЕ!</h1>
<p className="text-xl mb-6">
Исполняйте мечты с Eternos
</p>
<Button variant="secondary" size="lg">
Подробнее
</Button>
</div>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,97 @@
"use client"
import { useState } from "react"
import { Checkbox } from "./ui/checkbox"
import { Button } from "./ui/button"
import { Minus, Plus, Heart, Trash } from 'lucide-react'
import Image from "next/image"
const SAMPLE_ITEMS = [
{
id: 1,
name: "Бумага офисная SvetoCopy, 500 листов, А4",
price: 352,
oldPrice: 399,
image: "/placeholder.svg",
},
{
id: 2,
name: "Глюкометр Диаконт Концепт + тест-полоски",
price: 1450,
oldPrice: 1599,
image: "/placeholder.svg",
}
]
export function CartItems() {
const [selectedItems, setSelectedItems] = useState<number[]>([])
const toggleItem = (id: number) => {
setSelectedItems(prev =>
prev.includes(id)
? prev.filter(item => item !== id)
: [...prev, id]
)
}
return (
<div className="space-y-4">
<div className="flex items-center gap-2 mb-4">
<Checkbox
checked={selectedItems.length === SAMPLE_ITEMS.length}
onCheckedChange={(checked) => {
setSelectedItems(checked ? SAMPLE_ITEMS.map(item => item.id) : [])
}}
/>
<span>Выбрать все</span>
{selectedItems.length > 0 && (
<button className="text-red-500 ml-4 hover:underline">
Удалить выбранные
</button>
)}
</div>
{SAMPLE_ITEMS.map((item) => (
<div key={item.id} className="flex gap-4 p-4 bg-white rounded-lg">
<Checkbox
checked={selectedItems.includes(item.id)}
onCheckedChange={() => toggleItem(item.id)}
/>
<Image
src={item.image}
alt={item.name}
width={100}
height={100}
className="object-cover"
/>
<div className="flex-1">
<h3 className="font-medium">{item.name}</h3>
<div className="flex gap-4 mt-4">
<div className="flex items-center gap-2">
<Button variant="outline" size="icon">
<Minus className="h-4 w-4" />
</Button>
<span className="w-8 text-center">1</span>
<Button variant="outline" size="icon">
<Plus className="h-4 w-4" />
</Button>
</div>
<Button variant="ghost" size="icon">
<Heart className="h-4 w-4" />
</Button>
<Button variant="ghost" size="icon">
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
<div className="text-right">
<div className="text-lg font-bold">{item.price} </div>
<div className="text-sm text-muted-foreground line-through">
{item.oldPrice}
</div>
</div>
</div>
))}
</div>
)
}

View File

@@ -0,0 +1,27 @@
import { Button } from "./ui/button"
export function CartSummary() {
return (
<div className="bg-white p-4 rounded-lg">
<h2 className="text-lg font-semibold mb-4">Ваша корзина</h2>
<div className="space-y-2 mb-4">
<div className="flex justify-between">
<span>Товары (2)</span>
<span>10 236 </span>
</div>
<div className="flex justify-between text-green-600">
<span>Скидка</span>
<span>- 4 387 </span>
</div>
</div>
<div className="flex justify-between text-lg font-bold mb-4">
<span>С Ozon Картой</span>
<span>5 467 </span>
</div>
<Button className="w-full" size="lg">
Перейти к оформлению
</Button>
</div>
)
}

View File

@@ -0,0 +1,96 @@
"use client"
import * as React from "react"
import Link from "next/link"
import { Laptop, Smartphone, ShirtIcon, Home, Car, Baby, Dumbbell, BookOpen, Gift, MoreHorizontal } from 'lucide-react'
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from "@/components/ui/navigation-menu"
const categories = [
{
title: "Электроника",
icon: <Laptop className="w-5 h-5" />,
subcategories: ["Ноутбуки", "Планшеты", "Компьютеры", "Аксессуары"]
},
{
title: "Смартфоны и гаджеты",
icon: <Smartphone className="w-5 h-5" />,
subcategories: ["Телефоны", "Смарт-часы", "Наушники"]
},
{
title: "Одежда и обувь",
icon: <ShirtIcon className="w-5 h-5" />,
subcategories: ["Мужская одежда", "Женская одежда", "Обувь", "Аксессуары"]
},
{
title: "Дом и сад",
icon: <Home className="w-5 h-5" />,
subcategories: ["Мебель", "Текстиль", "Освещение", "Инструменты"]
},
{
title: "Авто",
icon: <Car className="w-5 h-5" />,
subcategories: ["Запчасти", "Шины", "Электроника", "Аксессуары"]
},
{
title: "Детские товары",
icon: <Baby className="w-5 h-5" />,
subcategories: ["Игрушки", "Питание", "Одежда", "Коляски"]
},
{
title: "Спорт и отдых",
icon: <Dumbbell className="w-5 h-5" />,
subcategories: ["Тренажеры", "Одежда", "Туризм"]
},
{
title: "Книги",
icon: <BookOpen className="w-5 h-5" />,
subcategories: ["Художественная", "Учебная", "Детская"]
},
{
title: "Подарки",
icon: <Gift className="w-5 h-5" />,
subcategories: ["Сувениры", "Цветы", "Подарочные наборы"]
},
]
export function CatalogMenu() {
return (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Каталог</NavigationMenuTrigger>
<NavigationMenuContent>
<div className="w-[500px] p-4">
<div className="grid gap-3">
{categories.map((category) => (
<Link
key={category.title}
href="#"
className="flex items-center gap-2 p-2 hover:bg-gray-100 rounded-md group"
>
<span className="group-hover:text-blue-600">{category.icon}</span>
<div className="flex-1">
<div className="font-medium group-hover:text-blue-600">{category.title}</div>
<div className="text-sm text-gray-500">
{category.subcategories.join(" • ")}
</div>
</div>
<MoreHorizontal className="w-5 h-5 text-gray-400" />
</Link>
))}
</div>
</div>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)
}

View File

@@ -0,0 +1,57 @@
import Link from "next/link"
import { Facebook, Instagram, Twitter } from 'lucide-react'
export function Footer() {
return (
<footer className="bg-white border-t mt-16">
<div className="container mx-auto px-4 py-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div>
<h3 className="font-semibold mb-4">О компании</h3>
<ul className="space-y-2">
<li><Link href="#" className="text-gray-600 hover:text-gray-900">О нас</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Вакансии</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Реквизиты</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Пресс-центр</Link></li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">Покупателям</h3>
<ul className="space-y-2">
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Как сделать заказ</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Способы оплаты</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Доставка</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Возврат товара</Link></li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">Партнерам</h3>
<ul className="space-y-2">
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Продавайте на Store</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Перевозчикам</Link></li>
<li><Link href="#" className="text-gray-600 hover:text-gray-900">Откройте пункт выдачи</Link></li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">Мы в соцсетях</h3>
<div className="flex space-x-4">
<Link href="#" className="text-gray-600 hover:text-gray-900">
<Facebook className="h-6 w-6" />
</Link>
<Link href="#" className="text-gray-600 hover:text-gray-900">
<Instagram className="h-6 w-6" />
</Link>
<Link href="#" className="text-gray-600 hover:text-gray-900">
<Twitter className="h-6 w-6" />
</Link>
</div>
</div>
</div>
<div className="border-t mt-8 pt-8 text-center text-gray-600">
<p>© 2024 Store. Все права защищены.</p>
</div>
</div>
</footer>
)
}

View File

@@ -0,0 +1,35 @@
import Link from "next/link"
import { Search } from "./search"
import { Button } from "./ui/button"
import { ShoppingCart, Heart, Package2 } from 'lucide-react'
import { CatalogMenu } from "./catalog-menu"
export function Header() {
return (
<header className="border-b sticky top-0 bg-white z-50">
<div className="container mx-auto px-4">
<div className="flex h-16 items-center justify-between">
<div className="flex items-center gap-8">
<Link href="/" className="text-2xl font-bold text-blue-600">
Eternos
</Link>
<CatalogMenu />
<Search />
</div>
<div className="flex items-center gap-4">
<Button variant="ghost" size="icon">
<Package2 className="h-5 w-5" />
</Button>
<Button variant="ghost" size="icon">
<Heart className="h-5 w-5" />
</Button>
<Button variant="ghost" size="icon">
<ShoppingCart className="h-5 w-5" />
</Button>
</div>
</div>
</div>
</header>
)
}

View File

@@ -0,0 +1,60 @@
import Image from "next/image"
import { Heart } from 'lucide-react'
import { Button } from "./ui/button"
import { Badge } from "./ui/badge"
interface ProductCardProps {
product: {
id: number
title: string
price: number
oldPrice: number
discount: number
image: string
isHotDeal?: boolean
isSale?: boolean
}
}
export function ProductCard({ product }: ProductCardProps) {
return (
<div className="group relative bg-white rounded-lg p-2 transition-shadow hover:shadow-lg">
<div className="relative aspect-square mb-2">
<Image
src={product.image}
alt={product.title}
fill
className="object-cover rounded-lg"
/>
<Button
variant="ghost"
size="icon"
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity"
>
<Heart className="h-5 w-5" />
</Button>
{product.isHotDeal && (
<div className="absolute top-2 left-2 bg-pink-500 text-white px-2 py-1 text-xs font-medium rounded">
НАРАСХВАТ
</div>
)}
</div>
<div className="space-y-2">
<div className="flex items-baseline gap-2">
<span className="text-xl font-bold">{product.price} </span>
<span className="text-sm text-muted-foreground line-through">
{product.oldPrice}
</span>
<span className="text-sm text-red-500">-{product.discount}%</span>
</div>
<h3 className="text-sm line-clamp-2">{product.title}</h3>
{product.isSale && (
<Badge variant="destructive" className="mt-2">
Распродажа
</Badge>
)}
</div>
</div>
)
}

View File

@@ -0,0 +1,80 @@
"use client"
import { useState } from "react"
import { Slider } from "@/components/ui/slider"
import { Checkbox } from "@/components/ui/checkbox"
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
export function ProductFilters() {
const [priceRange, setPriceRange] = useState([0, 10000])
return (
<div className="w-64 bg-white p-4 rounded-lg">
<h3 className="font-semibold mb-4">Фильтры</h3>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="price">
<AccordionTrigger>Цена</AccordionTrigger>
<AccordionContent>
<div className="space-y-4">
<Slider
defaultValue={[0, 10000]}
max={10000}
step={100}
value={priceRange}
onValueChange={setPriceRange}
className="mt-6"
/>
<div className="flex justify-between text-sm">
<span>{priceRange[0]} </span>
<span>{priceRange[1]} </span>
</div>
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="brand">
<AccordionTrigger>Бренд</AccordionTrigger>
<AccordionContent>
<div className="space-y-2">
<label className="flex items-center gap-2">
<Checkbox id="samsung" />
<span>Samsung</span>
</label>
<label className="flex items-center gap-2">
<Checkbox id="apple" />
<span>Apple</span>
</label>
<label className="flex items-center gap-2">
<Checkbox id="xiaomi" />
<span>Xiaomi</span>
</label>
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="availability">
<AccordionTrigger>Наличие</AccordionTrigger>
<AccordionContent>
<div className="space-y-2">
<label className="flex items-center gap-2">
<Checkbox id="in-stock" />
<span>В наличии</span>
</label>
<label className="flex items-center gap-2">
<Checkbox id="express-delivery" />
<span>Экспресс-доставка</span>
</label>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
}

View File

@@ -0,0 +1,70 @@
import { ProductCard } from "./product-card"
const SAMPLE_PRODUCTS = [
{
id: 1,
title: "Кофе растворимый Жокей Крепкий, 3 в 1, с сахаром",
price: 89,
oldPrice: 172,
discount: 48,
image: "/placeholder.svg",
isHotDeal: true,
isSale: true,
},
{
id: 2,
title: "Рексона Дезодорант женский твердый стик",
price: 235,
oldPrice: 397,
discount: 40,
image: "/placeholder.svg",
isHotDeal: true,
},
{
id: 3,
title: "Сухой корм Мираторг MEAT с нежной телятиной",
price: 187,
oldPrice: 294,
discount: 36,
image: "/placeholder.svg",
isHotDeal: true,
},
{
id: 4,
title: "Сухой корм KITEKAT™ для взрослых кошек «Мясной пир»",
price: 174,
oldPrice: 209,
discount: 16,
image: "/placeholder.svg",
isHotDeal: true,
},
{
id: 5,
title: "Специальное чистящее средство для стиральных машин",
price: 197,
oldPrice: 469,
discount: 57,
image: "/placeholder.svg",
isHotDeal: true,
},
{
id: 6,
title: "Крем для лица увлажняющий",
price: 184,
oldPrice: 413,
discount: 55,
image: "/placeholder.svg",
isHotDeal: true,
},
]
export function ProductGrid() {
return (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4">
{SAMPLE_PRODUCTS.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
)
}

View File

@@ -0,0 +1,17 @@
"use client"
import { SearchIcon } from 'lucide-react'
import { Input } from "./ui/input"
export function Search() {
return (
<div className="relative w-full max-w-lg">
<SearchIcon className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Искать на Store"
className="pl-8 w-full"
/>
</div>
)
}

View File

@@ -0,0 +1,58 @@
"use client"
import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const Accordion = AccordionPrimitive.Root
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
))
AccordionItem.displayName = "AccordionItem"
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
))
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
))
AccordionContent.displayName = AccordionPrimitive.Content.displayName
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

View File

@@ -0,0 +1,36 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
}
export { Badge, badgeVariants }

View File

@@ -0,0 +1,56 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@@ -0,0 +1,30 @@
"use client"
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from "@/lib/utils"
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox }

View File

@@ -0,0 +1,22 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }

View File

@@ -0,0 +1,128 @@
import * as React from "react"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority"
import { ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const NavigationMenu = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Root
ref={ref}
className={cn(
"relative z-10 flex max-w-max flex-1 items-center justify-center",
className
)}
{...props}
>
{children}
<NavigationMenuViewport />
</NavigationMenuPrimitive.Root>
))
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
const NavigationMenuList = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.List>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.List
ref={ref}
className={cn(
"group flex flex-1 list-none items-center justify-center space-x-1",
className
)}
{...props}
/>
))
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
const NavigationMenuItem = NavigationMenuPrimitive.Item
const navigationMenuTriggerStyle = cva(
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
)
const NavigationMenuTrigger = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Trigger
ref={ref}
className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props}
>
{children}{" "}
<ChevronDown
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
aria-hidden="true"
/>
</NavigationMenuPrimitive.Trigger>
))
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
const NavigationMenuContent = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Content
ref={ref}
className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
className
)}
{...props}
/>
))
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
const NavigationMenuLink = NavigationMenuPrimitive.Link
const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
className
)}
ref={ref}
{...props}
/>
</div>
))
NavigationMenuViewport.displayName =
NavigationMenuPrimitive.Viewport.displayName
const NavigationMenuIndicator = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Indicator
ref={ref}
className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
className
)}
{...props}
>
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
</NavigationMenuPrimitive.Indicator>
))
NavigationMenuIndicator.displayName =
NavigationMenuPrimitive.Indicator.displayName
export {
navigationMenuTriggerStyle,
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator,
NavigationMenuViewport,
}

View File

@@ -0,0 +1,28 @@
"use client"
import * as React from "react"
import * as SliderPrimitive from "@radix-ui/react-slider"
import { cn } from "@/lib/utils"
const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn(
"relative flex w-full touch-none select-none items-center",
className
)}
{...props}
>
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
<SliderPrimitive.Range className="absolute h-full bg-primary" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root>
))
Slider.displayName = SliderPrimitive.Root.displayName
export { Slider }

View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;

6569
frontend/style/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
{
"name": "style",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@nextui-org/button": "^2.2.9",
"@nextui-org/system": "^2.4.6",
"@nextui-org/theme": "^2.4.5",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-navigation-menu": "^1.2.3",
"@radix-ui/react-slider": "^1.2.2",
"@radix-ui/react-slot": "^1.1.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^11.16.0",
"lucide-react": "^0.469.0",
"next": "14.2.16",
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.16",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View File

@@ -0,0 +1,87 @@
import {nextui} from '@nextui-org/theme';
import type { Config } from "tailwindcss";
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./node_modules/@nextui-org/theme/dist/components/(button|ripple|spinner).js"
],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
},
keyframes: {
'accordion-down': {
from: {
height: '0'
},
to: {
height: 'var(--radix-accordion-content-height)'
}
},
'accordion-up': {
from: {
height: 'var(--radix-accordion-content-height)'
},
to: {
height: '0'
}
}
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out'
}
}
},
plugins: [require("tailwindcss-animate"),nextui()],
};
export default config;

View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}