Merge branch 'develop' into main

This commit is contained in:
Alexis
2021-03-29 15:19:19 +02:00
80 changed files with 11551 additions and 7435 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,21 +1,18 @@
module.exports = {
root: true,
env: {
browser: true,
node: true
},
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint"
],
parserOptions: {
ecmaVersion: 2020
parser: 'babel-eslint'
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"@typescript-eslint/no-explicit-any": "off",
}
};
extends: [
'@nuxtjs',
'plugin:nuxt/recommended'
],
plugins: [
],
// add your custom rules here
rules: {}
}

103
.gitignore vendored
View File

@@ -1,23 +1,90 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.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
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp

View File

@@ -1,16 +1,27 @@
# 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.
# full-skies
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
- [Vue](https://vuejs.org/) - Front-end framework
- [Vuex](https://vuex.vuejs.org/) - Store
<!-- - [MongoDB](https://www.mongodb.com/) - Database -->
* Vue.js
* Nuxt
* Axios
* Jest
## Usage
**TBI**
## Contributors
- [AlexisNP](https://github.com/AlexisNP)
## How to install
To install and run the project, you need to clone this project and run these commands in the root folder.
## Credits
**TBI**
```bash
# 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 'fonts';
@import 'variables';
@import 'animations';
* {
@@ -64,6 +63,13 @@ a {
color: inherit;
text-decoration: none;
}
&.fs-link {
color: $primary-xxlight;
text-decoration: underline;
&:hover {
color: $primary-xlight;
}
}
}
button {

View File

@@ -20,6 +20,9 @@ $danger: $red;
$primary: $space-blue-700;
$primary-light: $space-blue-600;
$primary-xlight: $space-blue-500;
$primary-xxlight: $space-blue-300;
$heading-font-family: 'Poppins', 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,42 +2,38 @@
<div class="navbar">
<ul class="navbar-menu no-style">
<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 }}
</router-link>
</nuxt-link>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
<script>
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
name: "navbar",
data() {
name: 'Navbar',
data () {
return {
items: [
{
text: "Home",
link: "/"
text: 'Home',
link: '/'
},
{
text: "Astres",
link: "/astres"
text: 'Astres',
link: '/astres'
},
{
text: "À propos",
link: "/a-propos"
},
{
text: "Profil",
link: "/profil"
text: 'Favoris',
link: '/favoris'
}
]
};
}
}
});
})
</script>
<style lang="scss" scoped>
@@ -92,7 +88,7 @@ export default defineComponent({
transition: width 0.25s ease-in-out;
}
&.router-link-active {
&.nuxt-link-exact-active {
&:after {
width: 100%;
}

View File

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

View File

@@ -0,0 +1,39 @@
<template>
<button
class="btn btn-primary"
:class="[
icon ? 'btn-icon' : ''
]"
>
<slot />
<span v-if="icon">
<img
:src="
white ? `/icons/${icon}.svg` : `/icons/${icon}-white.svg`
"
alt=""
class="fs-icon icon-sm"
>
</span>
</button>
</template>
<script>
export default {
name: 'FSButton',
props: {
icon: {
type: String,
default: () => { return '' }
},
white: {
type: Boolean,
default: () => { return false }
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,175 @@
<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.`,
link: {
text: 'Consultez vos favoris.',
url: '/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.`,
link: {
text: 'Consultez vos favoris.',
url: '/favoris'
},
category: 'valid',
timer: 3
})
}
this.$store.dispatch('toggleFav', this.celestial)
}
}
}
</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;
&:hover {
color: darken(#d01d35, 6%) !important
}
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<div class="card-icon">
<img
v-if="type === 'planète à lunes'"
src="/icons/planet-moons.svg"
class="fs-icon"
alt="planet icon"
>
<img
v-else-if="type === 'planète'"
src="/icons/planet.svg"
class="fs-icon"
alt="planet icon"
>
<img
v-else-if="type === 'lune'"
src="/icons/moon.svg"
class="fs-icon"
alt="moon icon"
>
<img
v-else-if="type === 'étoile'"
src="/icons/sun.svg"
class="fs-icon"
alt="moon icon"
>
<img
v-else
src="/icons/stars.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

@@ -0,0 +1,107 @@
<template>
<div class="celestial-filters">
<ul class="no-style">
<li>
<AtomsFSButton
:class="[filters.all ? 'active' : '']"
@click.native="emitFilter('all')"
>
Tous
</AtomsFSButton>
<AtomsFSButton
:class="[filters.planets ? 'active' : '']"
icon="planet"
inverted
@click.native="emitFilter('planets')"
>
Planètes
</AtomsFSButton>
<AtomsFSButton
:class="[filters.moons ? 'active' : '']"
icon="moon"
inverted
@click.native="emitFilter('moons')"
>
Lunes
</AtomsFSButton>
<AtomsFSButton
:class="[filters.stars ? 'active' : '']"
icon="sun"
inverted
@click.native="emitFilter('stars')"
>
Etoiles
</AtomsFSButton>
<AtomsFSButton
:class="[filters.others ? 'active' : '']"
icon="stars"
inverted
@click.native="emitFilter('others')"
>
Autres
</AtomsFSButton>
</li>
</ul>
</div>
</template>
<script>
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
name: 'CelestialFilters',
props: {
celestials: {
type: Array,
default: () => { return [] }
}
},
data () {
return {
filters: {
all: true,
planets: false,
moons: false,
stars: false,
others: false
}
}
},
methods: {
/**
* Emits the type of celestial body to filter for to parent
*/
emitFilter (type) {
// Reset filter appearance
this.filters = {
all: false,
planets: false,
moons: false,
stars: false,
others: false
}
// Triggers the correct filter
if (type === 'all') {
this.filters.all = true
} else if (type === 'planets') {
this.filters.planets = true
} else if (type === 'moons') {
this.filters.moons = true
} else if (type === 'stars') {
this.filters.stars = true
} else if (type === 'others') {
this.filters.others = true
}
this.$emit('filter:celestials', type)
}
}
})
</script>
<style lang="scss" scoped>
.celestial-filters {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,96 @@
<template>
<div class="celestial-list-wrapper">
<div v-if="celestials.length > 0">
<celestial-filters v-if="hasFilters" @filter:celestials="filterCelestials" />
<ul 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: flex-start;
align-items: stretch;
flex-flow: row wrap;
.celestial-item {
width: calc(33% - 20px);
margin-right: 20px;
margin-bottom: 20px;
}
}
</style>

View File

@@ -6,50 +6,54 @@
<div class="toast-message">
<slot name="message" />
</div>
<div class="toast-link">
<slot name="link" />
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import store from "@/store";
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
name: "ToastCard",
data() {
return {
closing: false
};
},
name: 'ToastCard',
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: {
// Counts down until 0 is reached
countdown(timeToLive) {
countdown (timeToLive) {
if (timeToLive > 0) {
console.log(timeToLive);
setTimeout(() => {
timeToLive = timeToLive - 1;
this.countdown(timeToLive);
}, 900);
timeToLive = timeToLive - 1
this.countdown(timeToLive)
}, this.tickingRate)
} else {
// When timer ends
this.closing = true;
this.closing = true
setTimeout(() => {
this.close();
}, 1200);
this.close()
}, this.tickingRate)
}
},
close() {
store.dispatch("toasts/purgeToast", this.toast.id);
close () {
this.$store.dispatch('toasts/purgeToast', this.toast.id)
}
},
mounted() {
// Starts countdown to initial timer value
this.countdown(this.toast.timer);
}
});
})
</script>
<style lang="scss" scoped>
@@ -61,11 +65,23 @@ export default defineComponent({
animation: fadeIn 1s cubic-bezier(0.175, 1, 0.32, 1),
slideFromRight 1s cubic-bezier(0.175, 1, 0.32, 1);
overflow: hidden;
.toast-title {
margin-bottom: 5px;
font-size: 16px;
font-weight: $fw-bold;
}
.toast-link {
a {
color: $primary-xxlight;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
&:after {
position: absolute;
display: block;
@@ -77,6 +93,7 @@ export default defineComponent({
height: 6px;
background: rgba($fs-black, 33%);
}
&.info {
background-color: $info;
}
@@ -90,6 +107,7 @@ export default defineComponent({
&.danger {
background-color: $danger;
}
&.closing {
animation: fadeOut 1.2s cubic-bezier(0.175, 1, 0.32, 1),
slideFromLeft 1.2s cubic-bezier(0.175, 1, 0.32, 1);

View File

@@ -1,32 +1,39 @@
<template>
<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">
<template #title>{{ toast.title }}</template>
<template #title>
{{ toast.title }}
</template>
<template #message>
{{ toast.message }}
</template>
<template #link>
<nuxt-link :to="toast.link.url">
{{ toast.link.text }}
</nuxt-link>
</template>
</toast-card>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import { defineComponent } from '@vue/composition-api'
import ToastCard from "./ToastCard.vue";
import ToastCard from './ToastCard.vue'
export default defineComponent({
name: "ToastsList",
name: 'ToastsList',
components: {
ToastCard
},
computed: {
queue() {
return this.$store.state.toasts.queue;
queue () {
return this.$store.state.toasts.queue
}
}
});
})
</script>
<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">
<navbar />
<div class="fs-content">
<router-view />
<Nuxt />
</div>
<toasts-list />
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import Navbar from "@/components/Navbar.vue";
import ToastsList from "@/components/toast/ToastsList.vue";
<script>
import ToastsList from '@/components/toast/ToastsList.vue'
export default defineComponent({
components: {
Navbar,
ToastsList
}
});
export default {
components: { ToastsList }
}
</script>
<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: {}
}

16565
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

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

@@ -0,0 +1,86 @@
<template>
<section class="celestial bg-image">
<main v-if="celestial">
<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">
<div 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 fs-link"
>
{{ 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>
</div>
</div>
</main>
</section>
</template>
<script>
// API
import { fetchCelestial } from '@/api/le-systeme-solaire'
export default {
name: 'Celestial',
data () {
return {
celestial: false
}
},
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");
}
}
</style>

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

@@ -0,0 +1,56 @@
<template>
<section class="celestials bg-image">
<main>
<header>
<h1 class="heading-1">
Le système solaire
</h1>
</header>
<div class="section-content">
<celestials-list :celestials="celestials" />
</div>
</main>
</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>

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

@@ -0,0 +1,45 @@
<template>
<section class="favourites bg-image">
<main>
<header>
<h1 class="heading-1">
Mes favoris
</h1>
</header>
<div class="section-content">
<div v-if="favourites && favourites.length >= 1">
<celestials-list :celestials="favourites" />
</div>
<div v-else>
<h3 class="heading-3">
Vous n'avez aucun favori pour le moment.
</h3>
</div>
</div>
</main>
</section>
</template>
<script>
export default {
name: 'Favourites',
computed: {
favourites () {
return 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>
<section class="home bg-image bg-fade-in">
<div class="splash fade-in-children">
<h2 class="heading-1">Full Skies</h2>
<h2 class="heading-1">
Full Skies
</h2>
<p class="heading-2">
Explorez les cieux et le système solaire avec Full Skies !
</p>
@@ -13,12 +15,10 @@
</section>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "Home"
});
<script>
export default {
name: 'Home'
}
</script>
<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,120 +0,0 @@
<template>
<div class="celestial-filters">
<ul class="no-style">
<li>
<button
class="btn btn-primary"
:class="[filters.all ? 'active' : '']"
@click="emitFilter('all')"
>
Tous
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.planets ? 'active' : '']"
@click="emitFilter('planets')"
>
Planètes
<img
src="icons/saturn-planet-shape-white.svg"
alt=""
class="fs-icon icon-sm"
/>
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.moons ? 'active' : '']"
@click="emitFilter('moons')"
>
Lunes
<img
src="icons/moon-and-stars-in-a-cloud-white.svg"
alt=""
class="fs-icon icon-sm"
/>
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.stars ? 'active' : '']"
@click="emitFilter('stars')"
>
Etoiles
<img src="icons/sun-shape-white.svg" alt="" class="fs-icon icon-sm" />
</button>
<button
class="btn btn-primary btn-icon"
:class="[filters.others ? 'active' : '']"
@click="emitFilter('others')"
>
Autres
<img
src="icons/stars-group-white.svg"
alt=""
class="fs-icon icon-sm"
/>
</button>
</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "celestial-filters",
props: {
celestials: {
type: Array
}
},
data() {
return {
filters: {
all: true,
planets: false,
moons: false,
stars: false,
others: false
}
};
},
methods: {
/**
* Emits the type of celestial body to filter for to parent
*/
emitFilter(type: string) {
// Reset filter appearance
this.filters = {
all: false,
planets: false,
moons: false,
stars: false,
others: false
};
// Triggers the correct filter
if (type == "all") {
this.filters.all = true;
} else if (type == "planets") {
this.filters.planets = true;
} else if (type == "moons") {
this.filters.moons = true;
} else if (type == "stars") {
this.filters.stars = true;
} else if (type == "others") {
this.filters.others = true;
}
// Emit filter type
this.$emit("filter:celestials", type);
}
}
});
</script>
<style lang="scss" scoped>
.celestial-filters {
margin-bottom: 20px;
}
</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;

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

@@ -1,8 +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,149 +0,0 @@
<template>
<div class="home">
<header class="profile-header">
<h2>My Profile</h2>
<hr />
<h2>Profile for {{ user.name }}</h2>
</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: [],
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: ""
}
};
},
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
);
}
},
mounted() {
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, celestial) => {
state.user.favourites.push(celestial)
},
removeFav: (state, celestial) => {
state.user.favourites = state.user.favourites.filter(
fav => fav.id !== celestial.id
)
}
}
export const actions = {
toggleFav: ({ commit, getters }, celestial) => {
// If the celestial is not faved
if (!getters.isFav(celestial.id)) {
commit('addFav', celestial)
} else {
commit('removeFav', celestial)
}
}
}
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";`
}
}
}
};