mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-13 19:06:26 +00:00
Merge pull request #19 from Reckless-Satoshi/order-book-first-iteration
Add book order API endpoint and basic frontend cards page
This commit is contained in:
commit
7e89f57779
35
api/admin.py
35
api/admin.py
@ -1,3 +1,36 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import Group, User
|
||||||
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
from .models import Order, Profile
|
||||||
|
|
||||||
# Register your models here.
|
admin.site.unregister(Group)
|
||||||
|
admin.site.unregister(User)
|
||||||
|
|
||||||
|
class ProfileInline(admin.StackedInline):
|
||||||
|
model = Profile
|
||||||
|
can_delete = False
|
||||||
|
fields = ('avatar_tag',)
|
||||||
|
readonly_fields = ['avatar_tag']
|
||||||
|
|
||||||
|
# extended users with avatars
|
||||||
|
@admin.register(User)
|
||||||
|
class EUserAdmin(UserAdmin):
|
||||||
|
inlines = [ProfileInline]
|
||||||
|
list_display = ('avatar_tag',) + UserAdmin.list_display
|
||||||
|
list_display_links = ['username']
|
||||||
|
def avatar_tag(self, obj):
|
||||||
|
return obj.profile.avatar_tag()
|
||||||
|
|
||||||
|
@admin.register(Order)
|
||||||
|
class OrderAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('id','type','maker','taker','status','amount','currency','created_at','expires_at')
|
||||||
|
list_display_links = ('maker','taker')
|
||||||
|
pass
|
||||||
|
|
||||||
|
@admin.register(Profile)
|
||||||
|
class UserProfileAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('avatar_tag','user','id','total_ratings','avg_rating','num_disputes','lost_disputes')
|
||||||
|
list_display_links =['user']
|
||||||
|
readonly_fields = ['avatar_tag']
|
||||||
|
pass
|
@ -1,11 +1,16 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator, validate_comma_separated_integer_list
|
||||||
|
from django.db.models.signals import post_save, pre_delete
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from django.utils.html import mark_safe
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
#############################
|
#############################
|
||||||
# TODO
|
# TODO
|
||||||
# Load hparams from .env file
|
# Load hparams from .env file
|
||||||
|
|
||||||
min_satoshis_trade = 10*1000
|
min_satoshis_trade = 10*1000
|
||||||
max_satoshis_trade = 500*1000
|
max_satoshis_trade = 500*1000
|
||||||
|
|
||||||
@ -45,6 +50,7 @@ class Order(models.Model):
|
|||||||
# order info, id = models.CharField(max_length=64, unique=True, null=False)
|
# order info, id = models.CharField(max_length=64, unique=True, null=False)
|
||||||
status = models.PositiveSmallIntegerField(choices=Status.choices, default=Status.WFB)
|
status = models.PositiveSmallIntegerField(choices=Status.choices, default=Status.WFB)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
expires_at = models.DateTimeField()
|
||||||
|
|
||||||
# order details
|
# order details
|
||||||
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False)
|
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False)
|
||||||
@ -56,7 +62,7 @@ class Order(models.Model):
|
|||||||
is_explicit = models.BooleanField(default=False, null=False) # pricing method. A explicit amount of sats, or a relative premium above/below market.
|
is_explicit = models.BooleanField(default=False, null=False) # pricing method. A explicit amount of sats, or a relative premium above/below market.
|
||||||
|
|
||||||
# order participants
|
# order participants
|
||||||
maker = models.ForeignKey(User, related_name='maker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a maker can only make one order
|
maker = models.ForeignKey(User, related_name='maker', on_delete=models.CASCADE, null=True, default=None) # unique = True, a maker can only make one order
|
||||||
taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a taker can only take one order
|
taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a taker can only take one order
|
||||||
|
|
||||||
# order collateral
|
# order collateral
|
||||||
@ -71,3 +77,40 @@ class Order(models.Model):
|
|||||||
# buyer payment LN invoice
|
# buyer payment LN invoice
|
||||||
has_invoice = models.BooleanField(default=False, null=False) # has invoice and is valid
|
has_invoice = models.BooleanField(default=False, null=False) # has invoice and is valid
|
||||||
invoice = models.CharField(max_length=300, unique=False, null=True, default=None)
|
invoice = models.CharField(max_length=300, unique=False, null=True, default=None)
|
||||||
|
|
||||||
|
class Profile(models.Model):
|
||||||
|
user = models.OneToOneField(User,on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
# Ratings stored as a comma separated integer list
|
||||||
|
total_ratings = models.PositiveIntegerField(null=False, default=0)
|
||||||
|
latest_ratings = models.CharField(max_length=999, null=True, default=None, validators=[validate_comma_separated_integer_list]) # Will only store latest ratings
|
||||||
|
avg_rating = models.DecimalField(max_digits=4, decimal_places=1, default=None, null=True, validators=[MinValueValidator(0), MaxValueValidator(100)])
|
||||||
|
|
||||||
|
# Disputes
|
||||||
|
num_disputes = models.PositiveIntegerField(null=False, default=0)
|
||||||
|
lost_disputes = models.PositiveIntegerField(null=False, default=0)
|
||||||
|
|
||||||
|
# RoboHash
|
||||||
|
avatar = models.ImageField(default="static/assets/avatars/unknown.png", verbose_name='Avatar')
|
||||||
|
|
||||||
|
@receiver(post_save, sender=User)
|
||||||
|
def create_user_profile(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
Profile.objects.create(user=instance)
|
||||||
|
|
||||||
|
@receiver(post_save, sender=User)
|
||||||
|
def save_user_profile(sender, instance, **kwargs):
|
||||||
|
instance.profile.save()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.user.username
|
||||||
|
|
||||||
|
# to display avatars in admin panel
|
||||||
|
def get_avatar(self):
|
||||||
|
if not self.avatar:
|
||||||
|
return 'static/assets/avatars/unknown.png'
|
||||||
|
return self.avatar.url
|
||||||
|
|
||||||
|
# method to create a fake table field in read only mode
|
||||||
|
def avatar_tag(self):
|
||||||
|
return mark_safe('<img src="%s" width="50" height="50" />' % self.get_avatar())
|
||||||
|
@ -4,7 +4,7 @@ from .models import Order
|
|||||||
class OrderSerializer(serializers.ModelSerializer):
|
class OrderSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Order
|
model = Order
|
||||||
fields = ('id','status','created_at','type','currency','amount','payment_method','is_explicit','premium','satoshis','maker','taker')
|
fields = ('id','status','created_at','expires_at','type','currency','amount','payment_method','is_explicit','premium','satoshis','maker','taker')
|
||||||
|
|
||||||
class MakeOrderSerializer(serializers.ModelSerializer):
|
class MakeOrderSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from .views import MakeOrder, OrderView, UserGenerator
|
from .views import MakeOrder, OrderView, UserGenerator, BookView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('make/', MakeOrder.as_view()),
|
path('make/', MakeOrder.as_view()),
|
||||||
path('order/', OrderView.as_view()),
|
path('order/', OrderView.as_view()),
|
||||||
path('usergen/', UserGenerator.as_view()),
|
path('usergen/', UserGenerator.as_view()),
|
||||||
|
path('book/', BookView.as_view()),
|
||||||
]
|
]
|
63
api/views.py
63
api/views.py
@ -3,6 +3,7 @@ from rest_framework.views import APIView
|
|||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.conf.urls.static import static
|
||||||
|
|
||||||
from .serializers import OrderSerializer, MakeOrderSerializer
|
from .serializers import OrderSerializer, MakeOrderSerializer
|
||||||
from .models import Order
|
from .models import Order
|
||||||
@ -17,6 +18,12 @@ from pathlib import Path
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
# .env
|
||||||
|
expiration_time = 8
|
||||||
|
|
||||||
|
avatar_path = Path('frontend/static/assets/avatars')
|
||||||
|
avatar_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
class MakeOrder(APIView):
|
class MakeOrder(APIView):
|
||||||
@ -51,6 +58,7 @@ class MakeOrder(APIView):
|
|||||||
premium=premium,
|
premium=premium,
|
||||||
satoshis=satoshis,
|
satoshis=satoshis,
|
||||||
is_explicit=is_explicit,
|
is_explicit=is_explicit,
|
||||||
|
expires_at= timezone.now()+timedelta(hours=expiration_time),
|
||||||
maker=request.user)
|
maker=request.user)
|
||||||
order.save()
|
order.save()
|
||||||
|
|
||||||
@ -96,7 +104,6 @@ class OrderView(APIView):
|
|||||||
|
|
||||||
return Response({'Bad Request':'Order ID parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'Bad Request':'Order ID parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class UserGenerator(APIView):
|
class UserGenerator(APIView):
|
||||||
lookup_url_kwarg = 'token'
|
lookup_url_kwarg = 'token'
|
||||||
NickGen = NickGenerator(
|
NickGen = NickGenerator(
|
||||||
@ -106,7 +113,7 @@ class UserGenerator(APIView):
|
|||||||
use_noun=True,
|
use_noun=True,
|
||||||
max_num=999)
|
max_num=999)
|
||||||
|
|
||||||
def get(self,request):
|
def get(self,request, format=None):
|
||||||
'''
|
'''
|
||||||
Get a new user derived from a high entropy token
|
Get a new user derived from a high entropy token
|
||||||
|
|
||||||
@ -139,18 +146,20 @@ class UserGenerator(APIView):
|
|||||||
|
|
||||||
# generate avatar
|
# generate avatar
|
||||||
rh = Robohash(hash)
|
rh = Robohash(hash)
|
||||||
rh.assemble(roboset='set1') # bgset='any' for backgrounds ON
|
rh.assemble(roboset='set1', bgset='any')# for backgrounds ON
|
||||||
|
|
||||||
avatars_path = Path('frontend/static/assets/avatars')
|
# Does not replace image if existing (avoid re-avatar in case of nick collusion)
|
||||||
avatars_path.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
with open(avatars_path.joinpath(nickname+".png"), "wb") as f:
|
image_path = avatar_path.joinpath(nickname+".png")
|
||||||
rh.img.save(f, format="png")
|
if not image_path.exists():
|
||||||
|
with open(image_path, "wb") as f:
|
||||||
|
rh.img.save(f, format="png")
|
||||||
|
|
||||||
# Create new credentials if nickname is new
|
# Create new credentials and logsin if nickname is new
|
||||||
if len(User.objects.filter(username=nickname)) == 0:
|
if len(User.objects.filter(username=nickname)) == 0:
|
||||||
User.objects.create_user(username=nickname, password=token, is_staff=False)
|
User.objects.create_user(username=nickname, password=token, is_staff=False)
|
||||||
user = authenticate(request, username=nickname, password=token)
|
user = authenticate(request, username=nickname, password=token)
|
||||||
|
user.profile.avatar = str(image_path)[9:] # removes frontend/ from url (ugly, to be fixed)
|
||||||
login(request, user)
|
login(request, user)
|
||||||
return Response(context, status=status.HTTP_201_CREATED)
|
return Response(context, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@ -159,17 +168,15 @@ class UserGenerator(APIView):
|
|||||||
if user is not None:
|
if user is not None:
|
||||||
login(request, user)
|
login(request, user)
|
||||||
# Sends the welcome back message, only if created +30 mins ago
|
# Sends the welcome back message, only if created +30 mins ago
|
||||||
if request.user.date_joined < (timezone.now()-timedelta(minutes=1)):
|
if request.user.date_joined < (timezone.now()-timedelta(minutes=30)):
|
||||||
context['found'] = 'We found your Robosat. Welcome back!'
|
context['found'] = 'We found your Robosat. Welcome back!'
|
||||||
return Response(context, status=status.HTTP_202_ACCEPTED)
|
return Response(context, status=status.HTTP_202_ACCEPTED)
|
||||||
else:
|
else:
|
||||||
# It is unlikely (1/20 Billions) but maybe the nickname is taken
|
# It is unlikely, but maybe the nickname is taken (1 in 20 Billion change)
|
||||||
context['found'] = 'Bad luck, this nickname is taken'
|
context['found'] = 'Bad luck, this nickname is taken'
|
||||||
context['bad_request'] = 'Enter a different token'
|
context['bad_request'] = 'Enter a different token'
|
||||||
return Response(context, status=status.HTTP_403_FORBIDDEN)
|
return Response(context, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def delete(self,request):
|
def delete(self,request):
|
||||||
user = User.objects.get(id = request.user.id)
|
user = User.objects.get(id = request.user.id)
|
||||||
|
|
||||||
@ -177,10 +184,40 @@ class UserGenerator(APIView):
|
|||||||
# However it might be a long time recovered user
|
# However it might be a long time recovered user
|
||||||
# Only delete if user live is < 5 minutes
|
# Only delete if user live is < 5 minutes
|
||||||
|
|
||||||
|
# TODO check if user exists AND it is not a maker or taker!
|
||||||
if user is not None:
|
if user is not None:
|
||||||
|
avatar_file = avatar_path.joinpath(str(request.user)+".png")
|
||||||
|
avatar_file.unlink() # Unsafe if avatar does not exist.
|
||||||
logout(request)
|
logout(request)
|
||||||
user.delete()
|
user.delete()
|
||||||
return Response(status=status.HTTP_301_MOVED_PERMANENTLY)
|
|
||||||
|
return Response({'user_deleted':'User deleted permanently'},status=status.HTTP_301_MOVED_PERMANENTLY)
|
||||||
|
|
||||||
return Response(status=status.HTTP_403_FORBIDDEN)
|
return Response(status=status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
class BookView(APIView):
|
||||||
|
serializer_class = OrderSerializer
|
||||||
|
|
||||||
|
def get(self,request, format=None):
|
||||||
|
currency = request.GET.get('currency')
|
||||||
|
type = request.GET.get('type')
|
||||||
|
queryset = Order.objects.filter(currency=currency, type=type, status=0) # TODO status = 1 for orders that are Public
|
||||||
|
if len(queryset)== 0:
|
||||||
|
return Response({'not_found':'No orders found, be the first to make one'}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
queryset = queryset.order_by('created_at')
|
||||||
|
book_data = []
|
||||||
|
for order in queryset:
|
||||||
|
data = OrderSerializer(order).data
|
||||||
|
user = User.objects.filter(id=data['maker'])
|
||||||
|
if len(user) == 1:
|
||||||
|
data['maker_nick'] = user[0].username
|
||||||
|
# TODO avoid sending status and takers for book views
|
||||||
|
#data.pop('status','taker')
|
||||||
|
book_data.append(data)
|
||||||
|
|
||||||
|
return Response(book_data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
313
frontend/package-lock.json
generated
313
frontend/package-lock.json
generated
@ -191,7 +191,6 @@
|
|||||||
"version": "7.16.7",
|
"version": "7.16.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
|
||||||
"integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
|
"integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/types": "^7.16.7"
|
"@babel/types": "^7.16.7"
|
||||||
}
|
}
|
||||||
@ -558,7 +557,6 @@
|
|||||||
"version": "7.16.7",
|
"version": "7.16.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
|
||||||
"integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
|
"integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/helper-plugin-utils": "^7.16.7"
|
"@babel/helper-plugin-utils": "^7.16.7"
|
||||||
}
|
}
|
||||||
@ -1149,11 +1147,132 @@
|
|||||||
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
|
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@emotion/babel-plugin": {
|
||||||
|
"version": "11.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
|
||||||
|
"integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-module-imports": "^7.12.13",
|
||||||
|
"@babel/plugin-syntax-jsx": "^7.12.13",
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@emotion/hash": "^0.8.0",
|
||||||
|
"@emotion/memoize": "^0.7.5",
|
||||||
|
"@emotion/serialize": "^1.0.2",
|
||||||
|
"babel-plugin-macros": "^2.6.1",
|
||||||
|
"convert-source-map": "^1.5.0",
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"find-root": "^1.1.0",
|
||||||
|
"source-map": "^0.5.7",
|
||||||
|
"stylis": "4.0.13"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
|
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/cache": {
|
||||||
|
"version": "11.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
|
||||||
|
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/memoize": "^0.7.4",
|
||||||
|
"@emotion/sheet": "^1.1.0",
|
||||||
|
"@emotion/utils": "^1.0.0",
|
||||||
|
"@emotion/weak-memoize": "^0.2.5",
|
||||||
|
"stylis": "4.0.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@emotion/hash": {
|
"@emotion/hash": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||||
},
|
},
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/memoize": "^0.7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/memoize": {
|
||||||
|
"version": "0.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
|
||||||
|
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
|
||||||
|
},
|
||||||
|
"@emotion/react": {
|
||||||
|
"version": "11.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.7.1.tgz",
|
||||||
|
"integrity": "sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@emotion/cache": "^11.7.1",
|
||||||
|
"@emotion/serialize": "^1.0.2",
|
||||||
|
"@emotion/sheet": "^1.1.0",
|
||||||
|
"@emotion/utils": "^1.0.0",
|
||||||
|
"@emotion/weak-memoize": "^0.2.5",
|
||||||
|
"hoist-non-react-statics": "^3.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/serialize": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/hash": "^0.8.0",
|
||||||
|
"@emotion/memoize": "^0.7.4",
|
||||||
|
"@emotion/unitless": "^0.7.5",
|
||||||
|
"@emotion/utils": "^1.0.0",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": {
|
||||||
|
"version": "3.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
|
||||||
|
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/sheet": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"version": "11.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.6.0.tgz",
|
||||||
|
"integrity": "sha512-mxVtVyIOTmCAkFbwIp+nCjTXJNgcz4VWkOYQro87jE2QBTydnkiYusMrRGFtzuruiGK4dDaNORk4gH049iiQuw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@emotion/babel-plugin": "^11.3.0",
|
||||||
|
"@emotion/is-prop-valid": "^1.1.1",
|
||||||
|
"@emotion/serialize": "^1.0.2",
|
||||||
|
"@emotion/utils": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/unitless": {
|
||||||
|
"version": "0.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||||
|
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||||
|
},
|
||||||
|
"@emotion/utils": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA=="
|
||||||
|
},
|
||||||
|
"@emotion/weak-memoize": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
|
||||||
|
},
|
||||||
"@material-ui/core": {
|
"@material-ui/core": {
|
||||||
"version": "4.12.3",
|
"version": "4.12.3",
|
||||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
|
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
|
||||||
@ -1230,6 +1349,65 @@
|
|||||||
"react-is": "^16.8.0 || ^17.0.0"
|
"react-is": "^16.8.0 || ^17.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@mui/private-theming": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-Lc1Cmu8lSsYZiXADi9PBb17Ho82ZbseHQujUFAcp6bCJ5x/d+87JYCIpCBMagPu/isRlFCwbziuXPmz7WOzJPQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.16.3",
|
||||||
|
"@mui/utils": "^5.2.3",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@mui/styled-engine": {
|
||||||
|
"version": "5.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.2.6.tgz",
|
||||||
|
"integrity": "sha512-bqAhli8eGS6v2qxivy2/4K0Ag8o//jsu1G2G6QcieFiT6y7oIF/nd/6Tvw6OSm3roOTifVQWNKwkt1yFWhGS+w==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.16.3",
|
||||||
|
"@emotion/cache": "^11.7.1",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@mui/system": {
|
||||||
|
"version": "5.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.2.6.tgz",
|
||||||
|
"integrity": "sha512-PZ7bmpWOLikWgqn2zWv9/Xa7lxnRBOmfjoMH7c/IVYJs78W3971brXJ3xV9MEWWQcoqiYQeXzUJaNf4rFbKCBA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.16.3",
|
||||||
|
"@mui/private-theming": "^5.2.3",
|
||||||
|
"@mui/styled-engine": "^5.2.6",
|
||||||
|
"@mui/types": "^7.1.0",
|
||||||
|
"@mui/utils": "^5.2.3",
|
||||||
|
"clsx": "^1.1.1",
|
||||||
|
"csstype": "^3.0.10",
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": {
|
||||||
|
"version": "3.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
|
||||||
|
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@mui/types": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ=="
|
||||||
|
},
|
||||||
|
"@mui/utils": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-sQujlajIS0zQKcGIS6tZR0L1R+ib26B6UtuEn+cZqwKHsPo3feuS+SkdscYBdcCdMbrZs4gj8WIJHl2z6tbSzQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.16.3",
|
||||||
|
"@types/prop-types": "^15.7.4",
|
||||||
|
"@types/react-is": "^16.7.1 || ^17.0.0",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^17.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/eslint": {
|
"@types/eslint": {
|
||||||
"version": "8.2.1",
|
"version": "8.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz",
|
||||||
@ -1268,6 +1446,11 @@
|
|||||||
"integrity": "sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA==",
|
"integrity": "sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/parse-json": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||||
|
},
|
||||||
"@types/prop-types": {
|
"@types/prop-types": {
|
||||||
"version": "15.7.4",
|
"version": "15.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
||||||
@ -1290,6 +1473,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-is": {
|
||||||
|
"version": "17.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz",
|
||||||
|
"integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-transition-group": {
|
"@types/react-transition-group": {
|
||||||
"version": "4.4.4",
|
"version": "4.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
||||||
@ -1554,6 +1745,16 @@
|
|||||||
"object.assign": "^4.1.0"
|
"object.assign": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"babel-plugin-macros": {
|
||||||
|
"version": "2.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
|
||||||
|
"integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.7.2",
|
||||||
|
"cosmiconfig": "^6.0.0",
|
||||||
|
"resolve": "^1.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"babel-plugin-polyfill-corejs2": {
|
"babel-plugin-polyfill-corejs2": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz",
|
||||||
@ -1619,6 +1820,11 @@
|
|||||||
"get-intrinsic": "^1.0.2"
|
"get-intrinsic": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"callsites": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||||
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001294",
|
"version": "1.0.30001294",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz",
|
||||||
@ -1707,7 +1913,6 @@
|
|||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
|
||||||
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
|
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "~5.1.1"
|
"safe-buffer": "~5.1.1"
|
||||||
},
|
},
|
||||||
@ -1715,8 +1920,7 @@
|
|||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1738,6 +1942,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cosmiconfig": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/parse-json": "^4.0.0",
|
||||||
|
"import-fresh": "^3.1.0",
|
||||||
|
"parse-json": "^5.0.0",
|
||||||
|
"path-type": "^4.0.0",
|
||||||
|
"yaml": "^1.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
@ -1824,6 +2040,14 @@
|
|||||||
"integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
|
"integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"error-ex": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||||
|
"requires": {
|
||||||
|
"is-arrayish": "^0.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"es-module-lexer": {
|
"es-module-lexer": {
|
||||||
"version": "0.9.3",
|
"version": "0.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
||||||
@ -1932,6 +2156,11 @@
|
|||||||
"pkg-dir": "^4.1.0"
|
"pkg-dir": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"find-root": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
|
||||||
|
},
|
||||||
"find-up": {
|
"find-up": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
@ -1945,8 +2174,7 @@
|
|||||||
"function-bind": {
|
"function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"gensync": {
|
"gensync": {
|
||||||
"version": "1.0.0-beta.2",
|
"version": "1.0.0-beta.2",
|
||||||
@ -1992,7 +2220,6 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"function-bind": "^1.1.1"
|
"function-bind": "^1.1.1"
|
||||||
}
|
}
|
||||||
@ -2048,6 +2275,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||||
},
|
},
|
||||||
|
"import-fresh": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||||
|
"requires": {
|
||||||
|
"parent-module": "^1.0.0",
|
||||||
|
"resolve-from": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"resolve-from": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"import-local": {
|
"import-local": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz",
|
||||||
@ -2064,11 +2307,15 @@
|
|||||||
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
|
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-arrayish": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||||
|
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
|
||||||
|
},
|
||||||
"is-core-module": {
|
"is-core-module": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
|
||||||
"integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
|
"integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
}
|
}
|
||||||
@ -2137,6 +2384,11 @@
|
|||||||
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
|
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"json-parse-even-better-errors": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||||
|
},
|
||||||
"json-schema-traverse": {
|
"json-schema-traverse": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
@ -2243,6 +2495,11 @@
|
|||||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lines-and-columns": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
|
},
|
||||||
"loader-runner": {
|
"loader-runner": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
||||||
@ -2435,6 +2692,25 @@
|
|||||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"parent-module": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||||
|
"requires": {
|
||||||
|
"callsites": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse-json": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.0.0",
|
||||||
|
"error-ex": "^1.3.1",
|
||||||
|
"json-parse-even-better-errors": "^2.3.0",
|
||||||
|
"lines-and-columns": "^1.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"path-exists": {
|
"path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
@ -2450,8 +2726,7 @@
|
|||||||
"path-parse": {
|
"path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"path-to-regexp": {
|
"path-to-regexp": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
@ -2461,6 +2736,11 @@
|
|||||||
"isarray": "0.0.1"
|
"isarray": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"path-type": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
|
||||||
|
},
|
||||||
"picocolors": {
|
"picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
@ -2667,7 +2947,6 @@
|
|||||||
"version": "1.20.0",
|
"version": "1.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
||||||
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-core-module": "^2.2.0",
|
"is-core-module": "^2.2.0",
|
||||||
"path-parse": "^1.0.6"
|
"path-parse": "^1.0.6"
|
||||||
@ -2787,6 +3066,11 @@
|
|||||||
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"stylis": {
|
||||||
|
"version": "4.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
|
||||||
|
"integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||||
@ -2991,6 +3275,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
||||||
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
|
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
"version": "1.10.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||||
|
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||||
|
"@emotion/react": "^11.7.1",
|
||||||
|
"@emotion/styled": "^11.6.0",
|
||||||
"@material-ui/core": "^4.12.3",
|
"@material-ui/core": "^4.12.3",
|
||||||
"@material-ui/icons": "^4.11.2",
|
"@material-ui/icons": "^4.11.2",
|
||||||
|
"@mui/system": "^5.2.6",
|
||||||
"material-ui-image": "^3.3.2",
|
"material-ui-image": "^3.3.2",
|
||||||
"react-router-dom": "^5.2.0"
|
"react-router-dom": "^5.2.0"
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,173 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
import { Paper, Button , Divider, Card, CardActionArea, CardContent, Typography, Grid, Select, MenuItem, FormControl, FormHelperText, List, ListItem, ListItemText, Avatar, Link, RouterLink, ListItemAvatar} from "@material-ui/core"
|
||||||
|
|
||||||
export default class BookPage extends Component {
|
export default class BookPage extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
this.state = {
|
||||||
|
orders: new Array(),
|
||||||
|
currency: 1,
|
||||||
|
type: 1,
|
||||||
|
};
|
||||||
|
this.getOrderDetails()
|
||||||
|
this.state.currencyCode = this.getCurrencyCode(this.state.currency)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
// Fix needed to handle HTTP 404 error when no order is found
|
||||||
return <p>This is the order book page</p>;
|
// Show message to be the first one to make an order
|
||||||
}
|
getOrderDetails() {
|
||||||
|
fetch('/api/book' + '?currency=' + this.state.currency + "&type=" + this.state.type)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => //console.log(data));
|
||||||
|
this.setState({orders: data}));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCardClick=(e)=>{
|
||||||
|
console.log(e.target)
|
||||||
|
this.props.history.push('/order/' + e.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make these two functions sequential. getOrderDetails needs setState to be finish beforehand.
|
||||||
|
handleTypeChange=(e)=>{
|
||||||
|
this.setState({
|
||||||
|
type: e.target.value,
|
||||||
|
});
|
||||||
|
this.getOrderDetails();
|
||||||
|
}
|
||||||
|
handleCurrencyChange=(e)=>{
|
||||||
|
this.setState({
|
||||||
|
currency: e.target.value,
|
||||||
|
currencyCode: this.getCurrencyCode(e.target.value),
|
||||||
|
})
|
||||||
|
this.getOrderDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets currency code (3 letters) from numeric (e.g., 1 -> USD)
|
||||||
|
// Improve this function so currencies are read from json
|
||||||
|
getCurrencyCode(val){
|
||||||
|
return (val == 1 ) ? "USD": ((val == 2 ) ? "EUR":"ETH")
|
||||||
|
}
|
||||||
|
|
||||||
|
// pretty numbers
|
||||||
|
pn(x) {
|
||||||
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Grid className='orderBook' container spacing={1}>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Typography component="h4" variant="h4">
|
||||||
|
Order Book
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid item xs={6} align="right">
|
||||||
|
<FormControl >
|
||||||
|
<FormHelperText>
|
||||||
|
I want to
|
||||||
|
</FormHelperText>
|
||||||
|
<Select
|
||||||
|
label="Select Order Type"
|
||||||
|
required="true"
|
||||||
|
value={this.state.type}
|
||||||
|
inputProps={{
|
||||||
|
style: {textAlign:"center"}
|
||||||
|
}}
|
||||||
|
onChange={this.handleTypeChange}
|
||||||
|
>
|
||||||
|
<MenuItem value={1}>BUY</MenuItem>
|
||||||
|
<MenuItem value={0}>SELL</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid item xs={6} align="left">
|
||||||
|
<FormControl >
|
||||||
|
<FormHelperText>
|
||||||
|
And pay with
|
||||||
|
</FormHelperText>
|
||||||
|
<Select
|
||||||
|
label="Select Payment Currency"
|
||||||
|
required="true"
|
||||||
|
value={this.state.currency}
|
||||||
|
inputProps={{
|
||||||
|
style: {textAlign:"center"}
|
||||||
|
}}
|
||||||
|
onChange={this.handleCurrencyChange}
|
||||||
|
>
|
||||||
|
<MenuItem value={1}>USD</MenuItem>
|
||||||
|
<MenuItem value={2}>EUR</MenuItem>
|
||||||
|
<MenuItem value={3}>ETH</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
{this.state.orders.map((order) =>
|
||||||
|
<Grid container item sm={4}>
|
||||||
|
<Card elevation={6} sx={{ width: 945 }}>
|
||||||
|
|
||||||
|
{/* To fix! does not pass order.id to handleCardCLick. Instead passes the clicked </>*/}
|
||||||
|
<CardActionArea value={order.id} onClick={this.handleCardClick}>
|
||||||
|
<CardContent>
|
||||||
|
|
||||||
|
<List dense="true">
|
||||||
|
<ListItem >
|
||||||
|
<ListItemAvatar >
|
||||||
|
<Avatar
|
||||||
|
alt={order.maker_nick}
|
||||||
|
src={window.location.origin +'/static/assets/avatars/' + order.maker_nick + '.png'}
|
||||||
|
/>
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText>
|
||||||
|
<Typography gutterBottom variant="h6">
|
||||||
|
{order.maker_nick}
|
||||||
|
</Typography>
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
{/* CARD PARAGRAPH CONTENT */}
|
||||||
|
<ListItemText>
|
||||||
|
<Typography variant="subtitle1" color="text.secondary">
|
||||||
|
◑{order.type == 0 ? <b> Buys </b>: <b> Sells </b>}
|
||||||
|
<b>{parseFloat(parseFloat(order.amount).toFixed(4))}
|
||||||
|
{" " +this.getCurrencyCode(order.currency)}</b> <a> worth of bitcoin</a>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="subtitle1" color="text.secondary">
|
||||||
|
◑ Payment via <b>{order.payment_method}</b>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="subtitle1" color="text.secondary">
|
||||||
|
◑ Priced {order.is_explicit ?
|
||||||
|
" explicitly at " + this.pn(order.satoshis) + " Sats" : (
|
||||||
|
" at " +
|
||||||
|
parseFloat(parseFloat(order.premium).toFixed(4)) + "% over the market"
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="subtitle1" color="text.secondary">
|
||||||
|
◑ <b>{" 42,354 "}{this.getCurrencyCode(order.currency)}/BTC</b> (Binance API)
|
||||||
|
</Typography>
|
||||||
|
</ListItemText>
|
||||||
|
|
||||||
|
</List>
|
||||||
|
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Typography component="h5" variant="h5">
|
||||||
|
You are {this.state.type == 0 ? " selling " : " buying "} BTC for {this.state.currencyCode}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Button color="secondary" variant="contained" to="/" component={Link}>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
import { Paper, Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
@ -79,7 +79,7 @@ export default class MakerPage extends Component {
|
|||||||
premium: 0,
|
premium: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
handleClickisExplicit=(e)=>{
|
handleClickExplicit=(e)=>{
|
||||||
this.setState({
|
this.setState({
|
||||||
isExplicit: true,
|
isExplicit: true,
|
||||||
satoshis: 10000,
|
satoshis: 10000,
|
||||||
@ -104,130 +104,116 @@ export default class MakerPage extends Component {
|
|||||||
};
|
};
|
||||||
fetch("/api/make/",requestOptions)
|
fetch("/api/make/",requestOptions)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => this.props.history.push('/order/' + data.id));
|
.then((data) => (console.log(data) & this.props.history.push('/order/' + data.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<Typography component="h4" variant="h4">
|
<Grid item xs={12} align="center">
|
||||||
Make an Order
|
<Typography component="h4" variant="h4">
|
||||||
</Typography>
|
Make an Order
|
||||||
</Grid>
|
</Typography>
|
||||||
<Grid item xs={12} align="center">
|
</Grid>
|
||||||
<FormControl component="fieldset">
|
<Paper elevation={12} style={{ padding: 8,}}>
|
||||||
<RadioGroup row defaultValue="0" onChange={this.handleTypeChange}>
|
<Grid item xs={12} align="center">
|
||||||
<FormControlLabel
|
<FormControl component="fieldset">
|
||||||
value="0"
|
<FormHelperText>
|
||||||
|
<div align='center'>
|
||||||
|
Choose Buy or Sell Bitcoin
|
||||||
|
</div>
|
||||||
|
</FormHelperText>
|
||||||
|
<RadioGroup row defaultValue="0" onChange={this.handleTypeChange}>
|
||||||
|
<FormControlLabel
|
||||||
|
value="0"
|
||||||
|
control={<Radio color="primary"/>}
|
||||||
|
label="Buy"
|
||||||
|
labelPlacement="Top"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
value="1"
|
||||||
|
control={<Radio color="secondary"/>}
|
||||||
|
label="Sell"
|
||||||
|
labelPlacement="Top"
|
||||||
|
/>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<FormControl >
|
||||||
|
<TextField
|
||||||
|
label="Amount of Fiat to Trade"
|
||||||
|
type="number"
|
||||||
|
required="true"
|
||||||
|
defaultValue={this.defaultAmount}
|
||||||
|
inputProps={{
|
||||||
|
min:0 ,
|
||||||
|
style: {textAlign:"center"}
|
||||||
|
}}
|
||||||
|
onChange={this.handleAmountChange}
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
label="Select Payment Currency"
|
||||||
|
required="true"
|
||||||
|
defaultValue={this.defaultCurrency}
|
||||||
|
inputProps={{
|
||||||
|
style: {textAlign:"center"}
|
||||||
|
}}
|
||||||
|
onChange={this.handleCurrencyChange}
|
||||||
|
>
|
||||||
|
<MenuItem value={1}>USD</MenuItem>
|
||||||
|
<MenuItem value={2}>EUR</MenuItem>
|
||||||
|
<MenuItem value={3}>ETH</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<FormControl >
|
||||||
|
<TextField
|
||||||
|
label="Payment Method(s)"
|
||||||
|
type="text"
|
||||||
|
require={true}
|
||||||
|
inputProps={{
|
||||||
|
style: {textAlign:"center"}
|
||||||
|
}}
|
||||||
|
onChange={this.handlePaymentMethodChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<FormControl component="fieldset">
|
||||||
|
<RadioGroup row defaultValue="relative">
|
||||||
|
<FormControlLabel
|
||||||
|
value="relative"
|
||||||
control={<Radio color="primary"/>}
|
control={<Radio color="primary"/>}
|
||||||
label="Buy"
|
label="Relative"
|
||||||
labelPlacement="Top"
|
labelPlacement="Top"
|
||||||
/>
|
onClick={this.handleClickRelative}
|
||||||
<FormControlLabel
|
/>
|
||||||
value="1"
|
<FormControlLabel
|
||||||
|
value="explicit"
|
||||||
control={<Radio color="secondary"/>}
|
control={<Radio color="secondary"/>}
|
||||||
label="Sell"
|
label="Explicit"
|
||||||
labelPlacement="Top"
|
labelPlacement="Top"
|
||||||
/>
|
onClick={this.handleClickExplicit}
|
||||||
</RadioGroup>
|
/>
|
||||||
<FormHelperText>
|
</RadioGroup>
|
||||||
<div align='center'>
|
<FormHelperText >
|
||||||
Choose Buy or Sell Bitcoin
|
<div align='center'>
|
||||||
</div>
|
Choose a Pricing Method
|
||||||
</FormHelperText>
|
</div>
|
||||||
</FormControl>
|
</FormHelperText>
|
||||||
</Grid>
|
</FormControl>
|
||||||
<Grid item xs={12} align="center">
|
</Grid>
|
||||||
<FormControl >
|
{/* conditional shows either Premium % field or Satoshis field based on pricing method */}
|
||||||
<Select
|
{ this.state.isExplicit
|
||||||
require={true}
|
? <Grid item xs={12} align="center">
|
||||||
defaultValue={this.defaultCurrency}
|
<TextField
|
||||||
inputProps={{
|
label="Explicit Amount in Satoshis"
|
||||||
style: {textAlign:"center"}
|
|
||||||
}}
|
|
||||||
onChange={this.handleCurrencyChange}
|
|
||||||
>
|
|
||||||
<MenuItem value={1}>USD</MenuItem>
|
|
||||||
<MenuItem value={2}>EUR</MenuItem>
|
|
||||||
<MenuItem value={3}>ETH</MenuItem>
|
|
||||||
</Select>
|
|
||||||
<FormHelperText>
|
|
||||||
<div align='center'>
|
|
||||||
Select Payment Currency
|
|
||||||
</div>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} align="center">
|
|
||||||
<FormControl >
|
|
||||||
<TextField
|
|
||||||
type="number"
|
|
||||||
require={true}
|
|
||||||
defaultValue={this.defaultAmount}
|
|
||||||
inputProps={{
|
|
||||||
min:0 ,
|
|
||||||
style: {textAlign:"center"}
|
|
||||||
}}
|
|
||||||
onChange={this.handleAmountChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormHelperText>
|
|
||||||
<div align='center'>
|
|
||||||
Amount of Fiat to Trade
|
|
||||||
</div>
|
|
||||||
</FormHelperText>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} align="center">
|
|
||||||
<FormControl >
|
|
||||||
<TextField
|
|
||||||
type="text"
|
|
||||||
require={true}
|
|
||||||
inputProps={{
|
|
||||||
style: {textAlign:"center"}
|
|
||||||
}}
|
|
||||||
onChange={this.handlePaymentMethodChange}
|
|
||||||
/>
|
|
||||||
<FormHelperText>
|
|
||||||
<div align='center'>
|
|
||||||
Enter the Payment Method(s)
|
|
||||||
</div>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} align="center">
|
|
||||||
<FormControl component="fieldset">
|
|
||||||
<RadioGroup row defaultValue="relative">
|
|
||||||
<FormControlLabel
|
|
||||||
value="relative"
|
|
||||||
control={<Radio color="primary"/>}
|
|
||||||
label="Relative"
|
|
||||||
labelPlacement="Top"
|
|
||||||
onClick={this.handleClickRelative}
|
|
||||||
/>
|
|
||||||
<FormControlLabel
|
|
||||||
value="explicit"
|
|
||||||
control={<Radio color="secondary"/>}
|
|
||||||
label="Explicit"
|
|
||||||
labelPlacement="Top"
|
|
||||||
onClick={this.handleClickisExplicit}
|
|
||||||
onShow="false"
|
|
||||||
/>
|
|
||||||
</RadioGroup>
|
|
||||||
<FormHelperText >
|
|
||||||
<div align='center'>
|
|
||||||
Choose a Pricing Method
|
|
||||||
</div>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</Grid>
|
|
||||||
{/* conditional shows either Premium % field or Satoshis field based on pricing method */}
|
|
||||||
{ this.state.isExplicit
|
|
||||||
? <Grid item xs={12} align="center">
|
|
||||||
<FormControl >
|
|
||||||
<TextField
|
|
||||||
type="number"
|
type="number"
|
||||||
require={true}
|
required="true"
|
||||||
inputProps={{
|
inputProps={{
|
||||||
// TODO read these from .env file
|
// TODO read these from .env file
|
||||||
min:10000 ,
|
min:10000 ,
|
||||||
@ -235,36 +221,25 @@ export default class MakerPage extends Component {
|
|||||||
style: {textAlign:"center"}
|
style: {textAlign:"center"}
|
||||||
}}
|
}}
|
||||||
onChange={this.handleSatoshisChange}
|
onChange={this.handleSatoshisChange}
|
||||||
defaultValue={this.defaultSatoshis}
|
// defaultValue={this.defaultSatoshis}
|
||||||
/>
|
/>
|
||||||
<FormHelperText>
|
</Grid>
|
||||||
<div align='center'>
|
: <Grid item xs={12} align="center">
|
||||||
Explicit Amount in Satoshis
|
|
||||||
</div>
|
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</Grid>
|
|
||||||
: <Grid item xs={12} align="center">
|
|
||||||
<FormControl >
|
|
||||||
<TextField
|
<TextField
|
||||||
|
label="Premium over Market (%)"
|
||||||
type="number"
|
type="number"
|
||||||
require={true}
|
// defaultValue={this.defaultPremium}
|
||||||
defaultValue={this.defaultPremium}
|
|
||||||
inputProps={{
|
inputProps={{
|
||||||
style: {textAlign:"center"}
|
style: {textAlign:"center"}
|
||||||
}}
|
}}
|
||||||
onChange={this.handlePremiumChange}
|
onChange={this.handlePremiumChange}
|
||||||
/>
|
/>
|
||||||
<FormHelperText>
|
</Grid>
|
||||||
<div align='center'>
|
}
|
||||||
Premium Relative to Market Price (%)
|
</Paper>
|
||||||
</div>
|
</Grid>
|
||||||
</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<Button color="primary" variant="contained" onClick={this.handleCreateOfferButtonPressed}>
|
<Button color="primary" variant="contained" onClick={this.handleCreateOfferButtonPressed} >
|
||||||
Create Order
|
Create Order
|
||||||
</Button>
|
</Button>
|
||||||
<Typography component="subtitle2" variant="subtitle2">
|
<Typography component="subtitle2" variant="subtitle2">
|
||||||
@ -277,14 +252,13 @@ export default class MakerPage extends Component {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
<Grid item xs={12} align="center">
|
||||||
<Grid item xs={12} align="center">
|
<Button color="secondary" variant="contained" to="/" component={Link}>
|
||||||
<Button color="secondary" variant="contained" to="/" component={Link}>
|
Back
|
||||||
Back
|
</Button>
|
||||||
</Button>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,12 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider} from "@material-ui/core"
|
import { Paper, Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider} from "@material-ui/core"
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
// pretty numbers
|
||||||
|
function pn(x) {
|
||||||
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||||
|
}
|
||||||
|
|
||||||
export default class OrderPage extends Component {
|
export default class OrderPage extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -36,27 +41,31 @@ export default class OrderPage extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix to use proper react props
|
||||||
|
handleClickBackButton=()=>{
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
render (){
|
render (){
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<Typography component="h5" variant="h5">
|
<Typography component="h5" variant="h5">
|
||||||
Robosat BTC {this.state.type ? " Sell " : " Buy "} Order
|
BTC {this.state.type ? " Sell " : " Buy "} Order
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Paper elevation={12} style={{ padding: 8,}}>
|
||||||
<List component="nav" aria-label="mailbox folders">
|
<List component="nav" aria-label="mailbox folders">
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemAvatar sx={{ width: 56, height: 56 }}>
|
<ListItemAvatar sx={{ width: 56, height: 56 }}>
|
||||||
<Avatar
|
<Avatar
|
||||||
alt={this.state.makerNick}
|
alt={this.state.makerNick}
|
||||||
src={window.location.origin +'/static/assets/avatars/' + this.state.makerNick + '.png'}
|
src={window.location.origin +'/static/assets/avatars/' + this.state.makerNick + '.png'}
|
||||||
sx={{ width: 56, height: 56 }}
|
|
||||||
/>
|
/>
|
||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText primary={this.state.makerNick} secondary="Order maker" />
|
<ListItemText primary={this.state.makerNick} secondary="Order maker" />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider />
|
<Divider />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText primary={this.state.amount+" "+this.state.currencyCode} secondary="Amount and currency requested"/>
|
<ListItemText primary={parseFloat(parseFloat(this.state.amount).toFixed(4))+" "+this.state.currencyCode} secondary="Amount and currency requested"/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider />
|
<Divider />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
@ -65,9 +74,9 @@ export default class OrderPage extends Component {
|
|||||||
<Divider />
|
<Divider />
|
||||||
<ListItem>
|
<ListItem>
|
||||||
{this.state.isExplicit ?
|
{this.state.isExplicit ?
|
||||||
<ListItemText primary={this.state.satoshis} secondary="Amount of Satoshis"/>
|
<ListItemText primary={pn(this.state.satoshis)} secondary="Amount of Satoshis"/>
|
||||||
:
|
:
|
||||||
<ListItemText primary={this.state.premium} secondary="Premium over market price"/>
|
<ListItemText primary={parseFloat(parseFloat(this.state.premium).toFixed(2))+"%"} secondary="Premium over market price"/>
|
||||||
}
|
}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -93,6 +102,10 @@ export default class OrderPage extends Component {
|
|||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
{this.state.isParticipant ? "" : <Button variant='contained' color='primary' to='/home' component={Link}>Take Order</Button>}
|
{this.state.isParticipant ? "" : <Button variant='contained' color='primary' to='/home' component={Link}>Take Order</Button>}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
{this.state.isParticipant ? "" : <Button variant='contained' color='secondary' onClick={this.handleClickBackButton}>Back</Button>}
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
import { Button , Grid, Typography, TextField, ButtonGroup} from "@material-ui/core"
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import Image from 'material-ui-image'
|
import Image from 'material-ui-image'
|
||||||
|
|
||||||
@ -85,9 +85,39 @@ export default class UserGenPage extends Component {
|
|||||||
this.getGeneratedUser();
|
this.getGeneratedUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TO FIX CSRF TOKEN IS NOT UPDATED UNTIL WINDOW IS RELOADED
|
||||||
|
reload_for_csrf_to_work=()=>{
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Grid container spacing={1}>
|
<Grid container spacing={1}>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Typography component="h5" variant="h5">
|
||||||
|
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<div style={{ maxWidth: 200, maxHeight: 200 }}>
|
||||||
|
<Image className='newAvatar'
|
||||||
|
disableError='true'
|
||||||
|
cover='true'
|
||||||
|
color='null'
|
||||||
|
src={this.state.avatar_url}
|
||||||
|
/>
|
||||||
|
</div><br/>
|
||||||
|
</Grid>
|
||||||
|
{
|
||||||
|
this.state.found ?
|
||||||
|
<Grid item xs={12} align="center">
|
||||||
|
<Typography component="subtitle2" variant="subtitle2" color='primary'>
|
||||||
|
{this.state.found}<br/>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
:
|
||||||
|
""
|
||||||
|
}
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<TextField
|
<TextField
|
||||||
error={this.state.bad_request}
|
error={this.state.bad_request}
|
||||||
@ -102,36 +132,19 @@ export default class UserGenPage extends Component {
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<div style={{ maxWidth: 200, maxHeight: 200 }}>
|
<Button onClick={this.handleAnotherButtonPressed}>Generate Another Robosat</Button>
|
||||||
<Image className='newAvatar'
|
</Grid>
|
||||||
disableError='true'
|
<Grid item xs={12} align="center">
|
||||||
cover='true'
|
<ButtonGroup variant="contained" aria-label="outlined primary button group">
|
||||||
color='null'
|
<Button color='primary' to='/make/' component={Link}>Make Order</Button>
|
||||||
src={this.state.avatar_url}
|
<Button to='/home' component={Link}>INFO</Button>
|
||||||
/>
|
<Button color='secondary' to='/book/' component={Link}>View Book</Button>
|
||||||
</div>
|
</ButtonGroup>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} align="center">
|
<Grid item xs={12} align="center">
|
||||||
<Typography component="h5" variant="h5">
|
<Typography component="h5" variant="h5">
|
||||||
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
|
Easy and Private Lightning peer-to-peer Exchange
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
|
||||||
{
|
|
||||||
this.state.found ?
|
|
||||||
<Grid item xs={12} align="center">
|
|
||||||
<Typography component="subtitle2" variant="subtitle2" color='primary'>
|
|
||||||
{this.state.found}<br/>
|
|
||||||
</Typography>
|
|
||||||
<Button variant='contained' color='primary' to='/home' component={Link}>Cool!</Button>
|
|
||||||
</Grid>
|
|
||||||
:
|
|
||||||
<Grid item xs={12} align="center">
|
|
||||||
<Button variant='contained' color='primary' to='/home' component={Link}>Take This Robosat!</Button>
|
|
||||||
</Grid>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Grid item xs={12} align="center">
|
|
||||||
<Button variant='contained' to='/' component={Link} onClick={this.handleAnotherButtonPressed}>Give Me Another</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
function getCookie(name) {
|
|
||||||
let cookieValue = null;
|
|
||||||
if (document.cookie && document.cookie !== '') {
|
|
||||||
const cookies = document.cookie.split(';');
|
|
||||||
for (let i = 0; i < cookies.length; i++) {
|
|
||||||
const cookie = cookies[i].trim();
|
|
||||||
// Does this cookie string begin with the name we want?
|
|
||||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
||||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cookieValue;
|
|
||||||
}
|
|
3
setup.md
3
setup.md
@ -47,7 +47,7 @@ pip install robohash
|
|||||||
### Install npm
|
### Install npm
|
||||||
`sudo apt install npm`
|
`sudo apt install npm`
|
||||||
|
|
||||||
No need to repeat, this is the list of npm packages we use
|
npm packages we use
|
||||||
```
|
```
|
||||||
cd frontend
|
cd frontend
|
||||||
npm init -y
|
npm init -y
|
||||||
@ -59,6 +59,7 @@ npm install @babel/plugin-proposal-class-properties
|
|||||||
npm install react-router-dom@5.2.0
|
npm install react-router-dom@5.2.0
|
||||||
npm install @material-ui/icons
|
npm install @material-ui/icons
|
||||||
npm install material-ui-image
|
npm install material-ui-image
|
||||||
|
npm install @mui/system @emotion/react @emotion/styled
|
||||||
```
|
```
|
||||||
|
|
||||||
### Launch the React render
|
### Launch the React render
|
||||||
|
Loading…
Reference in New Issue
Block a user