Merge branch 'develop-nuxt' into develop

This commit is contained in:
Alexis
2021-03-29 11:31:25 +02:00
79 changed files with 11395 additions and 7379 deletions

16
.babelrc Normal file
View File

@@ -0,0 +1,16 @@
{
"env": {
"test": {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
}
}

View File

@@ -1,3 +0,0 @@
> 1%
last 2 versions
not dead

13
.editorconfig Normal file
View File

@@ -0,0 +1,13 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

View File

@@ -1,22 +1,18 @@
module.exports = { module.exports = {
root: true, root: true,
env: { env: {
browser: true,
node: true node: true
}, },
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint"
],
parserOptions: { parserOptions: {
ecmaVersion: 2020 parser: 'babel-eslint'
}, },
rules: { extends: [
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off", '@nuxtjs',
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 'plugin:nuxt/recommended'
"@typescript-eslint/no-explicit-any": "off", ],
"@vue/no-deprecated-filter": "off", plugins: [
} ],
}; // add your custom rules here
rules: {}
}

103
.gitignore vendored
View File

@@ -1,23 +1,90 @@
.DS_Store # Created by .ignore support plugin (hsz.mobi)
node_modules ### Node template
/dist # Logs
/logs
*.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
# Editor directories and files # Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea .idea
.vscode
*.suo # Service worker
*.ntvs* sw.*
*.njsproj
*.sln # macOS
*.sw? .DS_Store
# Vim swap files
*.swp

View File

@@ -1,16 +1,27 @@
# Full Skies # full-skies
This small-scale application is used to consume the API. Its functionnalities for now are only about exploiting the API endpoints to display data. Ce projet a pour but d'exploiter [l'API Le Système Solaire](https://api.le-systeme-solaire.net/) et de proposer une interface simple pour selectionner des favoris.
In the future, users could be able to register and create their own list of astral bodies. - [Dependencies](#dependencies)
- [How to install](#how-to-install)
## Dependencies ## Dependencies
- [Vue](https://vuejs.org/) - Front-end framework * Vue.js
- [Vuex](https://vuex.vuejs.org/) - Store * Nuxt
<!-- - [MongoDB](https://www.mongodb.com/) - Database --> * Axios
* Jest
## Usage ## How to install
**TBI** To install and run the project, you need to clone this project and run these commands in the root folder.
## Contributors
- [AlexisNP](https://github.com/AlexisNP)
## Credits ```bash
**TBI** # Install the npm packages
$ npm install
# FOR DEVELOPPERS
# Runs an instance of the app on localhost:3000 that hot reloads
$ npm run dev
# FOR PRODUCTION
# Compiles and builds for production and starts app
$ npm run build
$ npm run start
```

View File

@@ -0,0 +1,40 @@
import axios from 'axios'
import { addCelestialType, addCelestialsType } from '@/plugins/methods'
/**
* Fetches the API for all celestial bodies and adds its type
* @returns Array of celestials in the API
*/
export const fetchCelestials = () => {
return axios
.get('https://api.le-systeme-solaire.net/rest/bodies/')
.then((res) => {
return addCelestialsType(res.data.bodies)
})
}
/**
* Fetches the API for all celestial bodies and adds its type
* @returns Array of celestials in the API
*/
export const fetchCelestial = (slug) => {
return axios
.get(`https://api.le-systeme-solaire.net/rest/bodies/${slug}`)
.then((res) => {
const celestial = addCelestialType(res.data)
const moons = []
if (celestial.moons) {
celestial.moons.forEach(async (moon) => {
const moonRes = await axios.get(moon.rel)
moons.push(addCelestialType(moonRes.data))
})
celestial.moons = moons
}
return celestial
})
}
export default {
fetchCelestials,
fetchCelestial
}

7
assets/README.md Normal file
View File

@@ -0,0 +1,7 @@
# ASSETS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).

2
assets/scss/_fonts.scss Normal file
View File

@@ -0,0 +1,2 @@
@import url(https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Round);
@import url(https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Poppins:wght@400;600;700;800;900&display=swap);

View File

@@ -1,7 +1,6 @@
@import 'libs/normalize'; @import 'libs/normalize';
@import 'fonts'; @import 'fonts';
@import 'variables';
@import 'animations'; @import 'animations';
* { * {

View File

@@ -20,6 +20,9 @@ $danger: $red;
$primary: $space-blue-700; $primary: $space-blue-700;
$primary-light: $space-blue-600; $primary-light: $space-blue-600;
$primary-xlight: $space-blue-500;
$primary-xxlight: $space-blue-300;
$heading-font-family: 'Poppins', serif; $heading-font-family: 'Poppins', serif;
$body-font-family: 'Open Sans', sans-serif; $body-font-family: 'Open Sans', sans-serif;

View File

@@ -1,3 +0,0 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"]
};

View File

@@ -2,34 +2,38 @@
<div class="navbar"> <div class="navbar">
<ul class="navbar-menu no-style"> <ul class="navbar-menu no-style">
<li v-for="item in items" :key="item.url" class="navbar-item"> <li v-for="item in items" :key="item.url" class="navbar-item">
<router-link :to="item.link" class="navbar-link no-style"> <nuxt-link :to="item.link" class="navbar-link no-style">
{{ item.text }} {{ item.text }}
</router-link> </nuxt-link>
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
<script lang="ts"> <script>
import { defineComponent } from "vue"; import { defineComponent } from '@vue/composition-api'
export default defineComponent({ export default defineComponent({
name: "navbar", name: 'Navbar',
data() { data () {
return { return {
items: [ items: [
{ {
text: "Home", text: 'Home',
link: "/" link: '/'
}, },
{ {
text: "Astres", text: 'Astres',
link: "/astres" link: '/astres'
},
{
text: 'Favoris',
link: '/favoris'
} }
] ]
};
} }
}); }
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -84,7 +88,7 @@ export default defineComponent({
transition: width 0.25s ease-in-out; transition: width 0.25s ease-in-out;
} }
&.router-link-active { &.nuxt-link-exact-active {
&:after { &:after {
width: 100%; width: 100%;
} }

View File

@@ -1,15 +1,15 @@
<template> <template>
<div class="fs-loader-wrapper"> <div class="fs-loader-wrapper">
<div class="fs-loader"></div> <div class="fs-loader" />
</div> </div>
</template> </template>
<script lang="ts"> <script>
import { defineComponent } from "vue"; import { defineComponent } from '@vue/composition-api'
export default defineComponent({ export default defineComponent({
name: "nest-loader" name: 'NestLoader'
}); })
</script> </script>
<style lang="scss"> <style lang="scss">

View File

View File

@@ -0,0 +1,164 @@
<template>
<div class="celestial-card">
<div class="card-wrapper">
<celestial-card-icon :type="celestial.type" />
<div class="card-header">
<h3 class="heading-3">
{{ celestial.name }}
</h3>
<p>
Type :
<span class="txt-capitalize">{{ celestial.type }}</span>
</p>
</div>
<div class="card-info">
<nuxt-link
:to="`/astres/${celestial.id}`"
class="no-style"
>
<span class="material-icons-round">info</span>
</nuxt-link>
</div>
<div class="card-content">
<div v-if="celestial.aroundPlanet">
<p>
Orbite autour de
<nuxt-link
:to="`/astres/${celestial.aroundPlanet.planet}`"
class="txt-capitalize"
>
{{ celestial.aroundPlanet.planet }}
</nuxt-link>
</p>
</div>
<div v-if="celestial.gravity">
<p>Facteur Gravité : {{ celestial.gravity }}</p>
</div>
<div v-if="celestial.density">
<p>Facteur Densité : {{ celestial.density }}</p>
</div>
</div>
<div v-if="celestial.discoveredBy" class="card-footer">
<p>
<i>
Ce corps céleste a été découvert par
{{ celestial.discoveredBy }} le {{ celestial.discoveryDate }}
</i>
</p>
</div>
<div class="card-actions">
<button
class="favourite no-style"
:class="isFav ? 'active' : ''"
@click="toggleFav()"
>
<span class="icon material-icons-round">favorite</span>
</button>
</div>
</div>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import CelestialCardIcon from './CelestialCardIcon.vue'
export default {
name: 'CelestialCard',
components: { CelestialCardIcon },
props: {
celestial: {
type: Object,
default: () => { return {} }
}
},
computed: {
isFav () {
return this.$store.getters.isFav(this.celestial.id)
}
},
methods: {
toggleFav () {
if (!this.isFav) {
this.$store.dispatch('toasts/pushToast', {
id: uuidv4(),
title: 'Favori ajouté',
message: `${this.celestial.name} a été ajouté à la liste des favoris.`,
category: 'valid',
timer: 3
})
} else {
this.$store.dispatch('toasts/pushToast', {
id: uuidv4(),
title: 'Favori retiré',
message: `${this.celestial.name} a été retiré de la liste des favoris.`,
category: 'valid',
timer: 3
})
}
this.$store.dispatch('toggleFav', this.celestial.id)
}
}
}
</script>
<style lang="scss" scoped>
.celestial-card {
height: 100%;
min-height: 100%;
padding: 20px;
color: $fs-black;
background: rgba($white, 85%);
border-radius: 5px;
.card-wrapper {
position: relative;
height: 100%;
display: flex;
flex-flow: column;
align-items: stretch;
> * {
flex: 0 1 auto;
}
.card-header {
padding-right: 60px;
}
.card-content {
flex: 1 1 auto;
min-height: 30px;
}
.card-info {
position: absolute;
top: 0;
right: 0;
}
.card-actions {
color: #afafaf;
.favourite {
.icon {
color: #afafaf;
}
&.active {
.icon {
position: relative;
display: inline-block;
will-change: font-size;
animation: toggleFavHeart 0.6s cubic-bezier(0.17, 0.89, 0.32, 1.49);
animation-fill-mode: forwards;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<div class="card-icon">
<img
v-if="type === 'planète à lunes'"
src="/icons/saturn-and-other-planets.svg"
class="fs-icon"
alt="planet icon"
>
<img
v-else-if="type === 'planète'"
src="/icons/saturn-planet-shape.svg"
class="fs-icon"
alt="planet icon"
>
<img
v-else-if="type === 'lune'"
src="/icons/moon-and-stars-in-a-cloud.svg"
class="fs-icon"
alt="moon icon"
>
<img
v-else-if="type === 'étoile'"
src="/icons/sun-shape.svg"
class="fs-icon"
alt="moon icon"
>
<img
v-else
src="/icons/stars-group.svg"
alt="moon icon"
class="fs-icon"
>
</div>
</template>
<script>
export default {
name: 'CelestialCardIcon',
props: {
type: {
type: String,
default: () => { return '' }
}
}
}
</script>
<style lang="scss">
.card-icon {
display: block;
position: absolute;
top: 20px;
bottom: 20px;
left: 0;
right: 0;
user-select: none;
pointer-events: none;
opacity: 4%;
img {
height: 100%;
width: 100%;
z-index: -1;
}
}
</style>

View File

@@ -19,7 +19,7 @@
src="icons/saturn-planet-shape-white.svg" src="icons/saturn-planet-shape-white.svg"
alt="" alt=""
class="fs-icon icon-sm" class="fs-icon icon-sm"
/> >
</button> </button>
<button <button
class="btn btn-primary btn-icon" class="btn btn-primary btn-icon"
@@ -31,7 +31,7 @@
src="icons/moon-and-stars-in-a-cloud-white.svg" src="icons/moon-and-stars-in-a-cloud-white.svg"
alt="" alt=""
class="fs-icon icon-sm" class="fs-icon icon-sm"
/> >
</button> </button>
<button <button
class="btn btn-primary btn-icon" class="btn btn-primary btn-icon"
@@ -39,7 +39,7 @@
@click="emitFilter('stars')" @click="emitFilter('stars')"
> >
Etoiles Etoiles
<img src="icons/sun-shape-white.svg" alt="" class="fs-icon icon-sm" /> <img src="icons/sun-shape-white.svg" alt="" class="fs-icon icon-sm">
</button> </button>
<button <button
class="btn btn-primary btn-icon" class="btn btn-primary btn-icon"
@@ -51,24 +51,25 @@
src="icons/stars-group-white.svg" src="icons/stars-group-white.svg"
alt="" alt=""
class="fs-icon icon-sm" class="fs-icon icon-sm"
/> >
</button> </button>
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
<script lang="ts"> <script>
import { defineComponent } from "vue"; import { defineComponent } from '@vue/composition-api'
export default defineComponent({ export default defineComponent({
name: "celestial-filters", name: 'CelestialFilters',
props: { props: {
celestials: { celestials: {
type: Array type: Array,
default: () => { return [] }
} }
}, },
data() { data () {
return { return {
filters: { filters: {
all: true, all: true,
@@ -77,13 +78,13 @@ export default defineComponent({
stars: false, stars: false,
others: false others: false
} }
}; }
}, },
methods: { methods: {
/** /**
* Emits the type of celestial body to filter for to parent * Emits the type of celestial body to filter for to parent
*/ */
emitFilter(type: string) { emitFilter (type) {
// Reset filter appearance // Reset filter appearance
this.filters = { this.filters = {
all: false, all: false,
@@ -91,25 +92,25 @@ export default defineComponent({
moons: false, moons: false,
stars: false, stars: false,
others: false others: false
}; }
// Triggers the correct filter // Triggers the correct filter
if (type == "all") { if (type === 'all') {
this.filters.all = true; this.filters.all = true
} else if (type == "planets") { } else if (type === 'planets') {
this.filters.planets = true; this.filters.planets = true
} else if (type == "moons") { } else if (type === 'moons') {
this.filters.moons = true; this.filters.moons = true
} else if (type == "stars") { } else if (type === 'stars') {
this.filters.stars = true; this.filters.stars = true
} else if (type == "others") { } else if (type === 'others') {
this.filters.others = true; this.filters.others = true
} }
this.$emit("filter:celestials", type); this.$emit('filter:celestials', type)
} }
} }
}); })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -0,0 +1,95 @@
<template>
<div class="celestial-list-wrapper">
<div v-if="celestials && celestials.length > 1">
<celestial-filters v-if="hasFilters" @filter:celestials="filterCelestials" />
<ul v-if="celestials" class="celestial-list grid no-style">
<li
v-for="(celestial, index) in sortedCelestials"
:key="index"
class="celestial-item"
>
<celestial-card :celestial="celestial" />
</li>
</ul>
</div>
<div v-else>
<nest-loader />
</div>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
import CelestialCard from '@/components/celestials/CelestialCard.vue'
import NestLoader from '@/components/NestLoader.vue'
import CelestialFilters from './CelestialFilters.vue'
export default defineComponent({
name: 'CelestialList',
components: {
CelestialCard,
CelestialFilters,
NestLoader
},
props: {
celestials: {
type: Array,
required: true
},
hasFilters: {
type: Boolean,
default: true
}
},
// Initial state
data () {
return {
activeFilter: 'all'
}
},
computed: {
sortedCelestials () {
if (this.activeFilter === 'moons') {
return this.celestials.filter(e => e.type === 'lune')
} else if (this.activeFilter === 'planets') {
return this.celestials.filter(
e => e.type === 'planète' || e.type === 'planète à lunes'
)
} else if (this.activeFilter === 'stars') {
return this.celestials.filter(e => e.type === 'étoile')
} else if (this.activeFilter === 'others') {
return this.celestials.filter(e => e.type === 'autre')
} else {
return this.celestials
}
}
},
methods: {
/**
* Applies the filter type to the data
*/
filterCelestials (type) {
this.activeFilter = type
}
}
})
</script>
<style lang="scss" scoped>
.celestial-list {
display: flex;
justify-content: space-between;
align-items: stretch;
flex-flow: row wrap;
.celestial-item {
width: calc(33% - 15px);
margin-bottom: 20px;
}
}
</style>

View File

@@ -10,46 +10,47 @@
</template> </template>
<script> <script>
import { defineComponent } from "vue"; import { defineComponent } from '@vue/composition-api'
import store from "@/store";
export default defineComponent({ export default defineComponent({
name: "ToastCard", name: 'ToastCard',
data() {
return {
closing: false
};
},
props: { props: {
toast: Object toast: {
type: Object,
default: () => { return {} }
}
},
data () {
return {
closing: false,
tickingRate: 900
}
},
mounted () {
// Starts countdown to initial timer value
this.countdown(this.toast.timer)
}, },
methods: { methods: {
// Counts down until 0 is reached // Counts down until 0 is reached
countdown(timeToLive) { countdown (timeToLive) {
if (timeToLive > 0) { if (timeToLive > 0) {
console.log(timeToLive);
setTimeout(() => { setTimeout(() => {
timeToLive = timeToLive - 1; timeToLive = timeToLive - 1
this.countdown(timeToLive); this.countdown(timeToLive)
}, 900); }, this.tickingRate)
} else { } else {
// When timer ends // When timer ends
this.closing = true; this.closing = true
setTimeout(() => { setTimeout(() => {
this.close(); this.close()
}, 1200); }, this.tickingRate)
} }
}, },
close() { close () {
store.dispatch("toasts/purgeToast", this.toast.id); this.$store.dispatch('toasts/purgeToast', this.toast.id)
} }
},
mounted() {
// Starts countdown to initial timer value
this.countdown(this.toast.timer);
} }
}); })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -1,8 +1,10 @@
<template> <template>
<div class="toast-wrapper"> <div class="toast-wrapper">
<div class="toast-item" v-for="toast in queue" :key="toast.id"> <div v-for="toast in queue" :key="toast.id" class="toast-item">
<toast-card :variant="toast.category" :toast="toast"> <toast-card :variant="toast.category" :toast="toast">
<template #title>{{ toast.title }}</template> <template #title>
{{ toast.title }}
</template>
<template #message> <template #message>
{{ toast.message }} {{ toast.message }}
</template> </template>
@@ -12,21 +14,21 @@
</template> </template>
<script> <script>
import { defineComponent } from "vue"; import { defineComponent } from '@vue/composition-api'
import ToastCard from "./ToastCard.vue"; import ToastCard from './ToastCard.vue'
export default defineComponent({ export default defineComponent({
name: "ToastsList", name: 'ToastsList',
components: { components: {
ToastCard ToastCard
}, },
computed: { computed: {
queue() { queue () {
return this.$store.state.toasts.queue; return this.$store.state.toasts.queue
} }
} }
}); })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

21
jest.config.js Normal file
View File

@@ -0,0 +1,21 @@
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js'
},
moduleFileExtensions: [
'js',
'vue',
'json'
],
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest'
},
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/components/**/*.vue',
'<rootDir>/pages/**/*.vue'
]
}

12
jsconfig.json Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"@/*": ["./*"],
"~~/*": ["./*"],
"@@/*": ["./*"]
}
},
"exclude": ["node_modules", ".nuxt", "dist"]
}

7
layouts/README.md Normal file
View File

@@ -0,0 +1,7 @@
# LAYOUTS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your Application Layouts.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).

View File

@@ -2,23 +2,18 @@
<div class="fs-wrapper"> <div class="fs-wrapper">
<navbar /> <navbar />
<div class="fs-content"> <div class="fs-content">
<router-view /> <Nuxt />
</div> </div>
<toasts-list /> <toasts-list />
</div> </div>
</template> </template>
<script lang="ts"> <script>
import { defineComponent } from "vue"; import ToastsList from '@/components/toast/ToastsList.vue'
import Navbar from "@/components/Navbar.vue";
import ToastsList from "@/components/toast/ToastsList.vue";
export default defineComponent({ export default {
components: { components: { ToastsList }
Navbar, }
ToastsList
}
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

8
middleware/README.md Normal file
View File

@@ -0,0 +1,8 @@
# MIDDLEWARE
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your application middleware.
Middleware let you define custom functions that can be run before rendering either a page or a group of pages.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).

53
nuxt.config.js Normal file
View File

@@ -0,0 +1,53 @@
export default {
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: 'Full Skies',
htmlAttrs: {
lang: 'fr'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'Application éducative et informative sur les astres du système solaire.' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
'@/assets/scss/_global.scss'
],
styleResources: {
scss: [
'@/assets/scss/_variables.scss'
]
},
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
// https://go.nuxtjs.dev/eslint
'@nuxtjs/eslint-module'
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios',
'@nuxtjs/style-resources'
],
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {}
}

16585
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,37 @@
{ {
"name": "full-skies", "name": "full-skies-nuxt",
"version": "0.1.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "dev": "nuxt",
"build": "vue-cli-service build", "build": "nuxt build",
"lint": "vue-cli-service lint" "start": "nuxt start",
"generate": "nuxt generate",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint": "npm run lint:js",
"test": "jest"
}, },
"dependencies": { "dependencies": {
"axios": "^0.21.1", "@nuxtjs/axios": "^5.12.5",
"core-js": "^3.6.5", "@nuxtjs/style-resources": "^1.0.0",
"uuid": "^8.3.2", "@vue/composition-api": "^1.0.0-rc.5",
"vue": "^3.0.0", "core-js": "^3.8.2",
"vue-router": "^4.0.0-0", "node-sass": "^5.0.0",
"vuex": "^4.0.0-0" "nuxt": "^2.14.12"
}, },
"devDependencies": { "devDependencies": {
"@types/uuid": "^8.3.0", "@nuxtjs/eslint-config": "^5.0.0",
"@typescript-eslint/eslint-plugin": "^2.33.0", "@nuxtjs/eslint-module": "^3.0.2",
"@typescript-eslint/parser": "^2.33.0", "@vue/test-utils": "^1.1.2",
"@vue/cli-plugin-babel": "~4.5.0", "babel-core": "7.0.0-bridge.0",
"@vue/cli-plugin-eslint": "~4.5.0", "babel-eslint": "^10.1.0",
"@vue/cli-plugin-router": "~4.5.0", "babel-jest": "^26.6.3",
"@vue/cli-plugin-typescript": "~4.5.0", "eslint": "^7.18.0",
"@vue/cli-plugin-vuex": "~4.5.0", "eslint-plugin-nuxt": "^2.0.0",
"@vue/cli-service": "~4.5.0", "eslint-plugin-vue": "^7.4.1",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^5.0.2",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-vue": "^7.0.0-0",
"prettier": "^1.19.1",
"sass": "^1.32.8", "sass": "^1.32.8",
"sass-loader": "10.1.1", "sass-loader": "^10.1.1",
"typescript": "~3.9.3" "jest": "^26.6.3",
"vue-jest": "^3.0.4"
} }
} }

6
pages/README.md Normal file
View File

@@ -0,0 +1,6 @@
# PAGES
This directory contains your Application Views and Routes.
The framework reads all the `*.vue` files inside this directory and creates the router of your application.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).

90
pages/astres/_slug.vue Normal file
View File

@@ -0,0 +1,90 @@
<template>
<section class="celestial bg-image">
<header>
<h1 class="heading-1">
<span>{{ celestial.name }}</span>
<span v-if="celestial.name != celestial.englishName">/ {{ celestial.englishName }}</span>
<span class="txt-capitalize">- {{ celestial.type }}</span>
</h1>
</header>
<div class="section-content">
<main v-if="celestial" class="celestial-body">
<div>
<h3 class="heading-3">
Informations
</h3>
<p v-if="celestial.aroundPlanet">
Orbite autour de
<nuxt-link
:to="`/astres/${celestial.aroundPlanet.planet}`"
class="txt-capitalize"
>
{{ celestial.aroundPlanet.planet }}
</nuxt-link>
</p>
<p v-if="celestial.discoveredBy">
Découvert par {{ celestial.discoveredBy }} le {{ celestial.discoveryDate }}
</p>
<p>Facteur Gravité : {{ celestial.gravity }}</p>
<p>Facteur Densité : {{ celestial.density }}</p>
<p>Inclinaison : {{ celestial.inclination }}</p>
</div>
<div v-if="celestial.moons" class="celestial-moons">
<h2 class="heading-2">
Astres orbitant {{ celestial.name }}
</h2>
<celestials-list :celestials="celestial.moons" :has-filters="false" />
</div>
</main>
</div>
</section>
</template>
<script>
// API
import { fetchCelestial } from '@/api/le-systeme-solaire'
export default {
name: 'Celestial',
data () {
return {
celestial: {}
}
},
mounted () {
// Fetches from API...
fetchCelestial(this.$route.params.slug)
.then((res) => {
this.celestial = res
return this.celestial
})
// .catch((err) => {
// console.error(err)
// })
}
}
</script>
<style lang="scss" scoped>
.celestial {
position: relative;
min-height: 100%;
padding: 25px 5%;
&:after {
background-image: url("/celestials_bg-min.jpg");
}
.celestial-body {
a {
color: $primary-xxlight;
text-decoration: underline;
&:hover {
color: $primary-xlight;
}
}
}
}
</style>

54
pages/astres/index.vue Normal file
View File

@@ -0,0 +1,54 @@
<template>
<section class="celestials bg-image">
<header>
<h1 class="heading-1">
Le système solaire
</h1>
</header>
<div class="section-content">
<celestials-list :celestials="celestials" />
</div>
</section>
</template>
<script>
// API
import { fetchCelestials } from '@/api/le-systeme-solaire'
import CelestialsList from '@/components/celestials/CelestialsList.vue'
export default {
name: 'Celestials',
components: {
CelestialsList
},
data () {
return {
celestials: []
}
},
mounted () {
fetchCelestials()
.then((res) => {
this.celestials = res
})
.catch(() => {
this.error = 'Impossible de récupérer les astres.'
})
}
}
</script>
<style lang="scss" scoped>
.celestials {
position: relative;
min-height: 100%;
padding: 25px 5%;
&:after {
background-image: url("/celestials_bg-min.jpg");
}
}
</style>

40
pages/favoris/index.vue Normal file
View File

@@ -0,0 +1,40 @@
<template>
<section class="favourites bg-image">
<header>
<h1 class="heading-1">
Mes favoris
</h1>
</header>
<div class="section-content">
<celestials-list :celestials="favourites" />
</div>
</section>
</template>
<script>
export default {
name: 'Favourites',
data () {
return {
favourites: []
}
},
created () {
// const favourites = this.$store.state.user.favourites
}
}
</script>
<style lang="scss">
.favourites {
position: relative;
min-height: 100%;
padding: 25px 5%;
&:after {
background-image: url("/home_bg-min.jpg");
}
}
</style>

View File

@@ -1,7 +1,9 @@
<template> <template>
<section class="home bg-image bg-fade-in"> <section class="home bg-image bg-fade-in">
<div class="splash fade-in-children"> <div class="splash fade-in-children">
<h2 class="heading-1">Full Skies</h2> <h2 class="heading-1">
Full Skies
</h2>
<p class="heading-2"> <p class="heading-2">
Explorez les cieux et le système solaire avec Full Skies ! Explorez les cieux et le système solaire avec Full Skies !
</p> </p>
@@ -13,12 +15,10 @@
</section> </section>
</template> </template>
<script lang="ts"> <script>
import { defineComponent } from "vue"; export default {
name: 'Home'
export default defineComponent({ }
name: "Home"
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

7
plugins/README.md Normal file
View File

@@ -0,0 +1,7 @@
# PLUGINS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains Javascript plugins that you want to run before mounting the root Vue.js application.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).

31
plugins/methods/index.js Normal file
View File

@@ -0,0 +1,31 @@
export const addCelestialType = (celestial) => {
if (celestial.isPlanet) {
if (celestial.moons) {
celestial.type = 'planète à lunes'
} else {
celestial.type = 'planète'
}
// Check if element is moon
} else if (celestial.aroundPlanet != null) {
celestial.type = 'lune'
// Check if element is star
} else if (celestial.id === 'soleil') {
celestial.type = 'étoile'
// ...else, body is "other"
} else {
celestial.type = 'autre'
}
return celestial
}
export const addCelestialsType = (celestials) => {
return celestials.map(e => addCelestialType(e))
}
export default {
addCelestialType,
addCelestialsType
}

View File

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -1,30 +0,0 @@
import axios from "axios";
/**
* Fetches the API for all celestial bodies
* @returns Array of celestials in the API
*/
export const fetchCelestials = (): Promise<any> => {
return axios
.get("https://api.le-systeme-solaire.net/rest/bodies/")
.then(res => {
return res.data.bodies;
});
};
/**
* Fetches the API for all celestial bodies
* @returns Array of celestials in the API
*/
export const fetchCelestial = (slug: string): Promise<any> => {
return axios
.get(`https://api.le-systeme-solaire.net/rest/bodies/${slug}`)
.then(res => {
return res.data;
});
};
export default {
fetchCelestials,
fetchCelestial
};

View File

@@ -1,36 +0,0 @@
import axios from "axios";
/**
* Get the excerpt of the wikipedia (fr) article for the specific celestial object
* @param celestial A celestial object
*/
export const fetchWikipediaExcerpt = async (celestial: any): Promise<any> => {
let query = 'https://fr.wikipedia.org/w/api.php?action=opensearch';
switch (celestial.id) {
case "soleil":
case "lune":
query += `&search=${celestial.id}`;
break;
case "planète à lunes":
query += `&search=${celestial.id}%20planète`;
break;
default:
query += `&search=${celestial.id}%20${celestial.type}`;
break;
}
query += "&limit=1&namespace=0&format=json";
console.log(query);
const req = await axios.get(query);
return;
};
export default {
fetchWikipediaExcerpt
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,2 +0,0 @@
@import url('https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Round');
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Poppins:wght@400;600;700;800;900&display=swap');

View File

@@ -1,217 +0,0 @@
<template>
<div class="celestial-card">
<div class="card-wrapper">
<div class="card-icon">
<img
v-if="celestial.type === 'planète à lunes'"
src="/icons/saturn-and-other-planets.svg"
class="fs-icon"
alt="planet icon"
/>
<img
v-else-if="celestial.type === 'planète'"
src="/icons/saturn-planet-shape.svg"
class="fs-icon"
alt="planet icon"
/>
<img
v-else-if="celestial.type === 'lune'"
src="icons/moon-and-stars-in-a-cloud.svg"
class="fs-icon"
alt="moon icon"
/>
<img
v-else-if="celestial.type === 'étoile'"
src="icons/sun-shape.svg"
class="fs-icon"
alt="moon icon"
/>
<img
v-else
src="icons/stars-group.svg"
alt="moon icon"
class="fs-icon"
/>
</div>
<div class="card-header">
<h3 class="heading-3">{{ celestial.name }}</h3>
<p>
Type :
<span class="txt-capitalize">{{ celestial.type }}</span>
</p>
</div>
<div class="card-info">
<router-link
:to="{ name: 'CelesteSingle', params: { slug: celestial.id } }"
class="no-style"
>
<span class="material-icons-round">info</span>
</router-link>
</div>
<div class="card-content">
<div v-if="celestial.aroundPlanet">
<p>
Orbite autour de
<router-link
:to="{
name: 'CelesteSingle',
params: { slug: celestial.aroundPlanet.planet }
}"
class="txt-capitalize"
>
{{ celestial.aroundPlanet.planet }}
</router-link>
</p>
</div>
<div v-if="celestial.gravity">
<p>Facteur Gravité : {{ celestial.gravity }}</p>
</div>
<div v-if="celestial.density">
<p>Facteur Densité : {{ celestial.density }}</p>
</div>
</div>
<div class="card-footer" v-if="celestial.discoveredBy">
<p>
<i>
Ce corps céleste a été découvert par
{{ celestial.discoveredBy }} le {{ celestial.discoveryDate }}
</i>
</p>
</div>
<div class="card-actions">
<button
@click="toggleFav(celestial)"
class="favourite no-style"
:class="isFav ? 'active' : ''"
>
<span class="icon material-icons-round">favorite</span>
</button>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { v4 as uuidv4 } from "uuid";
import store from "@/store";
export default defineComponent({
name: "celestial-card",
data() {
return {
type: "unknown"
};
},
computed: {
isFav() {
if (store.getters.isFav(this.celestial?.id)) {
return true;
}
return false;
}
},
props: {
celestial: Object
},
methods: {
toggleFav: (celestial: any) => {
store.dispatch("toggleFav", celestial.id);
if (store.getters.isFav(celestial.id)) {
store.dispatch("toasts/pushToast", {
id: uuidv4(),
title: "Favori ajouté",
message: `${celestial.name} a été ajouté à la liste des favoris.`,
category: "valid",
timer: 3
});
} else {
store.dispatch("toasts/pushToast", {
id: uuidv4(),
title: "Favori retiré",
message: `${celestial.name} a été retiré de la liste des favoris.`,
category: "valid",
timer: 3
});
}
}
}
});
</script>
<style lang="scss" scoped>
.celestial-card {
height: 100%;
min-height: 100%;
padding: 20px;
color: $fs-black;
background: rgba($white, 85%);
border-radius: 5px;
.card-wrapper {
position: relative;
height: 100%;
display: flex;
flex-flow: column;
align-items: stretch;
.card-icon {
display: block;
position: absolute;
top: 20px;
bottom: 20px;
left: 0;
right: 0;
user-select: none;
pointer-events: none;
opacity: 4%;
img {
height: 100%;
width: 100%;
z-index: -1;
}
}
> * {
flex: 0 1 auto;
}
.card-header {
padding-right: 60px;
}
.card-content {
flex: 1 1 auto;
min-height: 30px;
}
.card-info {
position: absolute;
top: 0;
right: 0;
}
.card-actions {
color: #afafaf;
.favourite {
.icon {
color: #afafaf;
}
&.active {
.icon {
position: relative;
display: inline-block;
will-change: font-size;
animation: toggleFavHeart 0.6s cubic-bezier(0.17, 0.89, 0.32, 1.49);
animation-fill-mode: forwards;
}
}
}
}
}
}
</style>

View File

@@ -1,82 +0,0 @@
<template>
<div class="celestial-list-wrapper">
<celestial-filters @filter:celestials="filterCelestials" />
<ul v-if="celestials" class="celestial-list grid no-style">
<li
v-for="(celestial, index) in sortedCelestials"
:key="index"
class="celestial-item"
>
<celestial-card :celestial="celestial" />
</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import CelestialCard from "@/components/celestials/CelestialCard.vue";
import CelestialFilters from "./CelestialFilters.vue";
export default defineComponent({
name: "celestial-list",
components: {
CelestialCard,
CelestialFilters
},
props: {
celestials: {
type: Array,
required: true
}
},
// Initial state
data() {
return {
activeFilter: "all"
};
},
computed: {
sortedCelestials(): Array<any> {
if (this.activeFilter == "moons") {
return this.celestials.filter((e: any) => e.type === "lune");
} else if (this.activeFilter == "planets") {
return this.celestials.filter(
(e: any) => e.type === "planète" || e.type === "planète à lunes"
);
} else if (this.activeFilter == "stars") {
return this.celestials.filter((e: any) => e.type === "étoile");
} else if (this.activeFilter == "others") {
return this.celestials.filter((e: any) => e.type === "autre");
} else {
return this.celestials;
}
}
},
methods: {
/**
* Applies the filter type to the data
*/
filterCelestials(type: string) {
this.activeFilter = type;
}
}
});
</script>
<style lang="scss" scoped>
.celestial-list {
display: flex;
justify-content: space-between;
align-items: stretch;
flex-flow: row wrap;
.celestial-item {
width: calc(33% - 15px);
margin-bottom: 20px;
}
}
</style>

View File

@@ -1,12 +0,0 @@
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "@/assets/scss/libs/_normalize.scss";
import "@/assets/scss/_global.scss";
createApp(App)
.use(store)
.use(router)
.mount("#app");

View File

@@ -1,31 +0,0 @@
export const addCelestialType = (celestial: any) => {
if (celestial.isPlanet) {
if (celestial.moons) {
celestial.type = "planète à lunes";
} else {
celestial.type = "planète";
}
// Check if element is moon
} else if (celestial.aroundPlanet != null) {
celestial.type = "lune";
// Check if element is star
} else if (celestial.id === "soleil") {
celestial.type = "étoile";
// ...else, body is "other"
} else {
celestial.type = "autre";
}
return celestial;
};
export const addCelestialsType = (celestials: any) => {
return celestials.map((e: any) => addCelestialType(e));
};
export default {
addCelestialType,
addCelestialsType
};

View File

@@ -1,44 +0,0 @@
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import Home from "../views/Home.vue";
import About from "../views/About.vue";
import Profile from "../views/Profile.vue";
import Celestials from "../views/celestials/Celestials.vue";
import Celestial from "../views/celestials/Celestial.vue";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/a-propos",
name: "About",
component: About
},
{
path: "/astres",
name: "CelesteAll",
component: Celestials
},
{
path: "/astres/:slug",
name: "CelesteSingle",
component: Celestial,
props: true
},
{
path: "/profil",
name: "Profile",
component: Profile
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;

7
src/shims-vue.d.ts vendored
View File

@@ -1,7 +0,0 @@
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'uuid';

View File

@@ -1,52 +0,0 @@
import { createStore } from "vuex";
import toastStore from "./toasts";
export default createStore({
state: () => ({
user: {
avatarUrl: "",
favourites: Array<{ id: string }>()
}
}),
getters: {
// Returns the number of favs for the user
favCount: (state): number => {
return state.user.favourites.length;
},
// Returns true if celestial is fav
isFav: state => (celestialId: string) => {
return state.user.favourites.find(fav => fav.id === celestialId);
}
},
mutations: {
addFav: (state, celestialId: string) => {
state.user.favourites.push({ id: celestialId });
},
removeFav: (state, celestialId: string) => {
state.user.favourites = state.user.favourites.filter(
fav => fav.id != celestialId
);
}
},
actions: {
toggleFav: ({ commit, getters }, celestialId: string) => {
// If the celestial is not faved
if (!getters.isFav(celestialId)) {
// ... favs it
commit("addFav", celestialId);
} else {
// ...else unfavs
commit("removeFav", celestialId);
}
}
},
modules: {
toasts: {
namespaced: true,
state: toastStore.state,
mutations: toastStore.mutations,
actions: toastStore.actions
}
}
});

View File

@@ -1,27 +0,0 @@
export default {
state: {
queue: Array<{
id: string;
title: string;
message: string;
category: string;
timer: number;
}>()
},
mutations: {
pushToast: (state: any, toast: any) => {
state.queue.push(toast);
},
purgeToast: (state: any, toastId: string) => {
state.queue = state.queue.filter((toast: any) => toast.id != toastId);
}
},
actions: {
pushToast: ({ commit }: any, toast: any) => {
commit("pushToast", toast);
},
purgeToast: ({ commit }: any, toastId: string) => {
commit("purgeToast", toastId);
}
}
};

View File

@@ -1,13 +0,0 @@
<template>
<section class="about">
<h2>About</h2>
</section>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "Home"
});
</script>

View File

@@ -1,163 +0,0 @@
<template>
<div class="home">
<header class="profile-header">
<h2>My Profile</h2>
<hr />
<h2>Profile for {{ user.name }}</h2>
<p>Inscrit le {{ registerDateFr }}</p>
</header>
<div v-if="user.avatar">
<img :src="user.avatar" alt="" class="avatar" />
</div>
<div v-if="user.repos">
{{ user.name }} possède {{ user.repos.length }} repos.
</div>
<div class="how-old">
<div v-if="user.age >= 20">
<p>Âge : {{ user.age }} (Extrêmement vieux)</p>
</div>
<div v-else-if="user.age >= 10 && user.age <= 20">
<p>Âge : {{ user.age }} (Enfant)</p>
</div>
<div v-else>
<p>Âge : {{ user.age }} (Doué pour utiliser un clavier)</p>
</div>
<div class="change-age">
<input type="number" v-model="user.age" min="1" max="999" />
<button>Devenir jeune</button>
<button>Devenir vieux</button>
</div>
</div>
<div class="my-friends">
<div class="friend-list">
<h2>Friends of {{ user.name }}</h2>
<table>
<thead>
<tr>
<th scope="col">Nom</th>
<th scope="col">UUID</th>
<th scope="col">Supprimer l'ami</th>
</tr>
</thead>
<tbody>
<tr v-for="friend in user.friends" :key="friend.uuid">
<td>{{ friend.name }}</td>
<td>{{ friend.uuid }}</td>
<td>
<button @click="removeFriend(friend.uuid)">
Détruire l'ami
</button>
</td>
</tr>
</tbody>
</table>
</div>
<form class="friend-actions" @submit.prevent="addFriend(inputFriend)">
<input type="text" v-model="inputFriend.name" />
<button type="submit">Ajouter l'ami</button>
</form>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import axios from "axios";
export default defineComponent({
name: "Profile",
data() {
return {
user: {
name: "Alexis",
avatar: null,
age: 21,
repos: [],
registerDate: "2020-10-30",
friends: [
{
uuid: "91261477-04fb-4052-8ef9-6041ad7afb7e",
name: "Me"
},
{
uuid: "c25880c8-4f04-432a-97bf-df639fff36e6",
name: "Myself"
},
{
uuid: "1cb4cdaf-6a37-49d4-a069-b8a516203cbd",
name: "I"
}
]
},
inputFriend: {
uuid: "bc59c1c9-b5a0-4047-a21e-afd1efcc4b64", // should be random but hey, this should be a token anyways so
name: ""
}
};
},
computed: {
registerDateFr(): any {
const data = new Date(this.user.registerDate);
const date = {
day: data.getDate(),
month: data.getUTCMonth() + 1,
year: data.getFullYear()
};
return `${date.day}/${date.month}/${date.year}`;
}
},
methods: {
addFriend() {
if (this.inputFriend.name) {
this.user.friends.push({ ...this.inputFriend });
this.inputFriend.name = "";
} else {
// todo: error handling
}
},
removeFriend(uuid: string) {
this.user.friends = this.user.friends.filter(
friend => friend.uuid != uuid
);
}
},
created() {
axios
.get("https://api.github.com/users/alexisnp")
.then(res => {
this.user.avatar = res.data.avatar_url;
})
.catch(err => {
console.log(err);
});
axios
.get("https://api.github.com/users/alexisnp/repos")
.then(res => {
this.user.repos = res.data;
})
.catch(err => {
console.log(err);
});
}
});
</script>
<style lang="scss">
.avatar {
display: block;
border-radius: 50%;
max-width: 250px;
max-height: 250px;
}
</style>

View File

@@ -1,79 +0,0 @@
<template>
<section class="celestial bg-image">
<div v-if="celestialLoaded">
<header>
<h1 class="heading-1">{{ celestial.name }}</h1>
</header>
<div class="section-content">
<div v-if="error">Une erreur est survenue : {{ error }}</div>
<div>{{ celestial }}</div>
</div>
</div>
<div v-else>
<nest-loader />
</div>
</section>
</template>
<script lang="ts">
import { defineComponent } from "vue";
// API
import { fetchCelestial } from "@/api/le-systeme-solaire";
// import { fetchWikipediaExcerpt } from "@/api/wikipedia";
// Global methods
import { addCelestialType } from "@/plugins/methods";
import NestLoader from "@/components/NestLoader.vue";
export default defineComponent({
components: {
NestLoader
},
name: "Celestial",
data() {
return {
celestial: {},
celestialLoaded: false,
excerpt: "",
error: ""
};
},
props: {
slug: {
type: String,
required: true
}
},
mounted() {
// Fetches from API...
fetchCelestial(this.slug)
.then(res => {
this.celestial = addCelestialType(res);
this.celestialLoaded = true;
return this.celestial;
})
.catch(() => {
this.error = "Impossible de récupérer l'astre demandé.";
})
.then(() => {
// fetchWikipediaExcerpt(celestial);
});
}
});
</script>
<style lang="scss" scoped>
.celestial {
position: relative;
min-height: 100%;
padding: 25px 5%;
&:after {
background-image: url("/celestials_bg-min.jpg");
}
}
</style>

View File

@@ -1,67 +0,0 @@
<template>
<section class="celestials bg-image">
<header>
<h1 class="heading-1">Le système solaire</h1>
</header>
<div class="section-content">
<div v-if="error">Une erreur est survenue : {{ error }}</div>
<div v-if="celestials.length > 1">
<celestials-list :celestials="celestials" />
</div>
<div v-else>
<nest-loader />
</div>
</div>
</section>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
// API
import { fetchCelestials } from "@/api/le-systeme-solaire";
// Global methods
import { addCelestialsType } from "@/plugins/methods";
import CelestialsList from "@/components/celestials/CelestialsList.vue";
import NestLoader from "@/components/NestLoader.vue";
export default defineComponent({
name: "Celestials",
components: {
CelestialsList,
NestLoader
},
data() {
return {
error: ""
};
},
setup() {
const celestials = ref(Array<any>());
const getCelestials = async () => {
celestials.value = await fetchCelestials();
celestials.value = addCelestialsType(celestials.value);
};
onMounted(getCelestials);
return { celestials };
}
});
</script>
<style lang="scss" scoped>
.celestials {
position: relative;
min-height: 100%;
padding: 25px 5%;
&:after {
background-image: url("/celestials_bg-min.jpg");
}
}
</style>

11
static/README.md Normal file
View File

@@ -0,0 +1,11 @@
# STATIC
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your static files.
Each file inside this directory is mapped to `/`.
Thus you'd want to delete this README.md before deploying to production.
Example: `/static/robots.txt` is mapped as `/robots.txt`.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 455 KiB

After

Width:  |  Height:  |  Size: 455 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 322 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 818 KiB

After

Width:  |  Height:  |  Size: 818 KiB

46
store/index.js Normal file
View File

@@ -0,0 +1,46 @@
export const state = () => ({
user: {
avatarUrl: '',
favourites: []
}
})
export const getters = {
// Returns the number of favs for the user
favCount: (state) => {
return state.user.favourites.length
},
// Returns true if celestial is fav
isFav: state => (celestialId) => {
return state.user.favourites.find(fav => fav.id === celestialId)
}
}
export const mutations = {
addFav: (state, celestialId) => {
state.user.favourites.push({ id: celestialId })
},
removeFav: (state, celestialId) => {
state.user.favourites = state.user.favourites.filter(
fav => fav.id !== celestialId
)
}
}
export const actions = {
toggleFav: ({ commit, getters }, celestialId) => {
// If the celestial is not faved
if (!getters.isFav(celestialId)) {
commit('addFav', celestialId)
} else {
commit('removeFav', celestialId)
}
}
}
export default {
state,
getters,
mutations,
actions
}

27
store/toasts.js Normal file
View File

@@ -0,0 +1,27 @@
export const state = () => ({
queue: []
})
export const mutations = {
pushToast: (state, toast) => {
state.queue.push(toast)
},
purgeToast: (state, toastId) => {
state.queue = state.queue.filter(toast => toast.id !== toastId)
}
}
export const actions = {
pushToast: ({ commit }, toast) => {
commit('pushToast', toast)
},
purgeToast: ({ commit }, toastId) => {
commit('purgeToast', toastId)
}
}
export default {
state,
mutations,
actions
}

9
test/Logo.spec.js Normal file
View File

@@ -0,0 +1,9 @@
import { mount } from '@vue/test-utils'
import Logo from '@/components/Logo.vue'
describe('Logo', () => {
test('is a Vue instance', () => {
const wrapper = mount(Logo)
expect(wrapper.vm).toBeTruthy()
})
})

View File

@@ -1,39 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,9 +0,0 @@
module.exports = {
css: {
loaderOptions: {
scss: {
additionalData: `@import "src/assets/scss/_variables.scss";`
}
}
}
};