Merge pull request 'feature/vendor' (#1) from feature/vendor into main
Reviewed-on: http://172.16.100.2:3000/mike/django/pulls/1
This commit is contained in:
commit
79ec088059
|
@ -1,3 +1,18 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
# Register your models here.
|
||||
from .models import VendorsData, CryptoCoins, VendorsAddresses
|
||||
|
||||
class VendorDetails(admin.ModelAdmin):
|
||||
fields = ["vendor"]
|
||||
list_display = ("vendorid_id", "vendor", "vendorWebName", "vendorWebAddr", "vendorAddDate", "vendorIsActive")
|
||||
|
||||
class CryptoDetails(admin.ModelAdmin):
|
||||
list_display = ("coinName", "coinSymbol", "coinAddDate", "coinIsActive" )
|
||||
|
||||
class VendorAddressDetails(admin.ModelAdmin):
|
||||
list_display = ("coin", "address", "vendor_id", "vendorid_id", "addrIsActive")
|
||||
|
||||
admin.site.register(VendorsData, VendorDetails)
|
||||
admin.site.register(CryptoCoins, CryptoDetails)
|
||||
admin.site.register(VendorsAddresses, VendorAddressDetails)
|
|
@ -0,0 +1,12 @@
|
|||
from django.forms import ModelForm
|
||||
from .models import VendorsData, VendorsAddresses
|
||||
|
||||
class VendorEditForm(ModelForm):
|
||||
class Meta:
|
||||
model = VendorsData
|
||||
fields = ['vendorCoverAmount', 'vendorPaidNotification', 'vendorPayWindow', 'vendorWebName', 'vendorWebAddr', 'vendorNetworkFee']
|
||||
|
||||
class VendorAddrAddForm(ModelForm):
|
||||
class Meta:
|
||||
model = VendorsAddresses
|
||||
fields = ['coin', 'address']
|
|
@ -0,0 +1,629 @@
|
|||
import string, hashlib, binascii, random, socket, base58, sys
|
||||
from typing import Union
|
||||
from urllib.parse import urlparse
|
||||
from ipaddress import ip_address, ip_network, IPv4Address
|
||||
from base58 import b58decode_check, b58encode_check
|
||||
from enum import Enum
|
||||
|
||||
def vendor_generator(size=6, chars=string.ascii_uppercase + string.digits):
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
|
||||
|
||||
def decodeBase58(address):
|
||||
decoded = base58.b58decode(address).hex()
|
||||
prefixAndHash = decoded[:len(decoded)-8]
|
||||
checksum = decoded[len(decoded)-8:]
|
||||
hash = prefixAndHash
|
||||
for _ in range(1,3):
|
||||
hash = hashlib.sha256(binascii.unhexlify(hash)).hexdigest()
|
||||
if(checksum == hash[:8]):
|
||||
return True
|
||||
return False
|
||||
|
||||
def decodeMonero(address):
|
||||
# decode monero address
|
||||
if len(address) == 95 or len(address) == 106:
|
||||
# Decode the address from Base58
|
||||
decoded = decode(address)
|
||||
|
||||
# Verify address type byte
|
||||
if decoded[0:2] not in ["12", "2a", "13"]:
|
||||
return False
|
||||
|
||||
# Verify checksum
|
||||
# later
|
||||
return True
|
||||
return False
|
||||
|
||||
def checksumCheck(method, address):
|
||||
match method.lower():
|
||||
case 'btc':
|
||||
return decodeBase58(address) if address[0] == '1' or address[0] == '3' else True if address[0:3] == 'bc1' and decode("bc", address)[0] != None else False
|
||||
case 'btct':
|
||||
return decodeBase58(address) if address[0] == '2' else True if address[0:3] == 'tb1' and decode("tb", address)[0] != None else False
|
||||
case 'ltc':
|
||||
return decodeBase58(address) if address[0] == '3' or address[0] == 'M' or address[0] == 'L' else True if address[0:4] == 'ltc1' and decode("ltc", address)[0] != None else False
|
||||
case 'bch':
|
||||
return is_valid(address) if address[0] == '1' else True if is_valid('bitcoincash:'+address) == True else False
|
||||
case 'zec':
|
||||
return decodeBase58(address) if address[0] == 't' or address[0] == 'z' else False
|
||||
case 'xmr':
|
||||
#needs new function to check if address is valid, a decoder maybe
|
||||
return decodeMonero(address)
|
||||
case _:
|
||||
return False
|
||||
|
||||
#########################################################################
|
||||
## UrlValidator
|
||||
#########################################################################
|
||||
|
||||
class UrlValidator:
|
||||
@staticmethod
|
||||
def is_internal_address(ip: Union[IPv4Address]) -> bool:
|
||||
return any([
|
||||
ip.is_private,
|
||||
ip.is_unspecified,
|
||||
ip.is_reserved,
|
||||
ip.is_loopback,
|
||||
ip.is_multicast,
|
||||
ip.is_link_local,
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def validate(cls, url: str):
|
||||
DEFAULT_PORT_WHITELIST = {80, 81, 8080, 443, 8443, 8000}
|
||||
DEFAULT_SCHEME_WHITELIST = {'http', 'https'}
|
||||
DEFAULT_HOST_BLACKLIST = {'192.0.0.192', '169.254.169.254', '100.100.100.200', 'metadata.packet.net', 'metadata.google.internal'}
|
||||
DEFAULT_CHARACTER_WHITELIST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:/-_.?&='
|
||||
|
||||
if url is None:
|
||||
return False
|
||||
|
||||
whitelist_set = set(DEFAULT_CHARACTER_WHITELIST)
|
||||
if any(c not in whitelist_set for c in url):
|
||||
return False
|
||||
|
||||
try:
|
||||
ip = ip_address(url)
|
||||
except ValueError:
|
||||
try:
|
||||
host = urlparse(url).hostname
|
||||
ip = ip_address(str(socket.gethostbyname(host)))
|
||||
except:
|
||||
return False
|
||||
|
||||
port_whitelist = DEFAULT_PORT_WHITELIST.copy()
|
||||
scheme_whitelist = DEFAULT_SCHEME_WHITELIST.copy()
|
||||
host_blacklist = DEFAULT_HOST_BLACKLIST.copy()
|
||||
|
||||
try:
|
||||
port, scheme = urlparse(url).port, urlparse(url).scheme
|
||||
except:
|
||||
return False
|
||||
|
||||
if scheme_whitelist and scheme is not None and scheme not in scheme_whitelist:
|
||||
return False
|
||||
|
||||
if host_blacklist and host is not None and host in host_blacklist:
|
||||
return False
|
||||
|
||||
if port_whitelist and port is not None and port not in port_whitelist:
|
||||
return False
|
||||
|
||||
if ip.version == 4:
|
||||
if not ip.is_private:
|
||||
# CGNAT IPs do not set `is_private` so `not is_global` added
|
||||
if not ip_network(ip).is_global:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
if cls.is_internal_address(ip):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
"""Reference implementation for Bech32/Bech32m and segwit addresses."""
|
||||
#########################################################################
|
||||
## SEGWIT
|
||||
#########################################################################
|
||||
|
||||
class Encoding(Enum):
|
||||
"""Enumeration type to list the various supported encodings."""
|
||||
BECH32 = 1
|
||||
BECH32M = 2
|
||||
|
||||
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||
BECH32M_CONST = 0x2bc830a3
|
||||
|
||||
def bech32_polymod(values):
|
||||
"""Internal function that computes the Bech32 checksum."""
|
||||
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
|
||||
chk = 1
|
||||
for value in values:
|
||||
top = chk >> 25
|
||||
chk = (chk & 0x1ffffff) << 5 ^ value
|
||||
for i in range(5):
|
||||
chk ^= generator[i] if ((top >> i) & 1) else 0
|
||||
return chk
|
||||
|
||||
|
||||
def bech32_hrp_expand(hrp):
|
||||
"""Expand the HRP into values for checksum computation."""
|
||||
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
|
||||
|
||||
|
||||
def bech32_verify_checksum(hrp, data):
|
||||
"""Verify a checksum given HRP and converted data characters."""
|
||||
const = bech32_polymod(bech32_hrp_expand(hrp) + data)
|
||||
if const == 1:
|
||||
return Encoding.BECH32
|
||||
if const == BECH32M_CONST:
|
||||
return Encoding.BECH32M
|
||||
return None
|
||||
|
||||
def bech32_create_checksum(hrp, data, spec):
|
||||
"""Compute the checksum values given HRP and data."""
|
||||
values = bech32_hrp_expand(hrp) + data
|
||||
const = BECH32M_CONST if spec == Encoding.BECH32M else 1
|
||||
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
|
||||
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
|
||||
|
||||
|
||||
def bech32_encode(hrp, data, spec):
|
||||
"""Compute a Bech32 string given HRP and data values."""
|
||||
combined = data + bech32_create_checksum(hrp, data, spec)
|
||||
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
||||
|
||||
def bech32_decode(bech):
|
||||
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
|
||||
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
|
||||
(bech.lower() != bech and bech.upper() != bech)):
|
||||
return (None, None, None)
|
||||
bech = bech.lower()
|
||||
pos = bech.rfind('1')
|
||||
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
|
||||
return (None, None, None)
|
||||
if not all(x in CHARSET for x in bech[pos+1:]):
|
||||
return (None, None, None)
|
||||
hrp = bech[:pos]
|
||||
data = [CHARSET.find(x) for x in bech[pos+1:]]
|
||||
spec = bech32_verify_checksum(hrp, data)
|
||||
if spec is None:
|
||||
return (None, None, None)
|
||||
return (hrp, data[:-6], spec)
|
||||
|
||||
def convertbits(data, frombits, tobits, pad=True):
|
||||
"""General power-of-2 base conversion."""
|
||||
acc = 0
|
||||
bits = 0
|
||||
ret = []
|
||||
maxv = (1 << tobits) - 1
|
||||
max_acc = (1 << (frombits + tobits - 1)) - 1
|
||||
for value in data:
|
||||
if value < 0 or (value >> frombits):
|
||||
return None
|
||||
acc = ((acc << frombits) | value) & max_acc
|
||||
bits += frombits
|
||||
while bits >= tobits:
|
||||
bits -= tobits
|
||||
ret.append((acc >> bits) & maxv)
|
||||
if pad:
|
||||
if bits:
|
||||
ret.append((acc << (tobits - bits)) & maxv)
|
||||
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
|
||||
return None
|
||||
return ret
|
||||
|
||||
|
||||
def decode(hrp, addr):
|
||||
"""Decode a segwit address."""
|
||||
hrpgot, data, spec = bech32_decode(addr)
|
||||
if hrpgot != hrp:
|
||||
return (None, None)
|
||||
decoded = convertbits(data[1:], 5, 8, False)
|
||||
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
|
||||
return (None, None)
|
||||
if data[0] > 16:
|
||||
return (None, None)
|
||||
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
||||
return (None, None)
|
||||
if data[0] == 0 and spec != Encoding.BECH32 or data[0] != 0 and spec != Encoding.BECH32M:
|
||||
return (None, None)
|
||||
return (data[0], decoded)
|
||||
|
||||
|
||||
def encode(hrp, witver, witprog):
|
||||
"""Encode a segwit address."""
|
||||
spec = Encoding.BECH32 if witver == 0 else Encoding.BECH32M
|
||||
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), spec)
|
||||
if decode(hrp, ret) == (None, None):
|
||||
return None
|
||||
return ret
|
||||
|
||||
#########################################################################
|
||||
## CRYPTO
|
||||
#########################################################################
|
||||
CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
|
||||
|
||||
def polymod(values):
|
||||
chk = 1
|
||||
generator = [
|
||||
(0x01, 0x98f2bc8e61),
|
||||
(0x02, 0x79b76d99e2),
|
||||
(0x04, 0xf33e5fb3c4),
|
||||
(0x08, 0xae2eabe2a8),
|
||||
(0x10, 0x1e4f43e470)]
|
||||
for value in values:
|
||||
top = chk >> 35
|
||||
chk = ((chk & 0x07ffffffff) << 5) ^ value
|
||||
for i in generator:
|
||||
if top & i[0] != 0:
|
||||
chk ^= i[1]
|
||||
return chk ^ 1
|
||||
|
||||
|
||||
def prefix_expand(prefix):
|
||||
return [ord(x) & 0x1f for x in prefix] + [0]
|
||||
|
||||
|
||||
def calculate_checksum(prefix, payload):
|
||||
poly = polymod(prefix_expand(prefix) + payload + [0, 0, 0, 0, 0, 0, 0, 0])
|
||||
out = list()
|
||||
for i in range(8):
|
||||
out.append((poly >> 5 * (7 - i)) & 0x1f)
|
||||
return out
|
||||
|
||||
|
||||
def verify_checksum(prefix, payload):
|
||||
return polymod(prefix_expand(prefix) + payload) == 0
|
||||
|
||||
|
||||
def b32decode(inputs):
|
||||
out = list()
|
||||
for letter in inputs:
|
||||
out.append(CHARSET.find(letter))
|
||||
return out
|
||||
|
||||
|
||||
def b32encode(inputs):
|
||||
out = ''
|
||||
for char_code in inputs:
|
||||
out += CHARSET[char_code]
|
||||
return out
|
||||
|
||||
|
||||
def convertbits(data, frombits, tobits, pad=True):
|
||||
acc = 0
|
||||
bits = 0
|
||||
ret = []
|
||||
maxv = (1 << tobits) - 1
|
||||
max_acc = (1 << (frombits + tobits - 1)) - 1
|
||||
for value in data:
|
||||
if value < 0 or (value >> frombits):
|
||||
return None
|
||||
acc = ((acc << frombits) | value) & max_acc
|
||||
bits += frombits
|
||||
while bits >= tobits:
|
||||
bits -= tobits
|
||||
ret.append((acc >> bits) & maxv)
|
||||
if pad:
|
||||
if bits:
|
||||
ret.append((acc << (tobits - bits)) & maxv)
|
||||
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
|
||||
return None
|
||||
return ret
|
||||
|
||||
|
||||
#########################################################################
|
||||
## BCH CONVERT
|
||||
#########################################################################
|
||||
class InvalidAddress(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Address:
|
||||
VERSION_MAP = {
|
||||
'legacy': [
|
||||
('P2SH', 5, False),
|
||||
('P2PKH', 0, False),
|
||||
('P2SH-TESTNET', 196, True),
|
||||
('P2PKH-TESTNET', 111, True)
|
||||
],
|
||||
'cash': [
|
||||
('P2SH', 8, False),
|
||||
('P2PKH', 0, False),
|
||||
('P2SH-TESTNET', 8, True),
|
||||
('P2PKH-TESTNET', 0, True)
|
||||
]
|
||||
}
|
||||
MAINNET_PREFIX = 'bitcoincash'
|
||||
TESTNET_PREFIX = 'bchtest'
|
||||
|
||||
def __init__(self, version, payload, prefix=None):
|
||||
self.version = version
|
||||
self.payload = payload
|
||||
if prefix:
|
||||
self.prefix = prefix
|
||||
else:
|
||||
if Address._address_type('cash', self.version)[2]:
|
||||
self.prefix = self.TESTNET_PREFIX
|
||||
else:
|
||||
self.prefix = self.MAINNET_PREFIX
|
||||
|
||||
def __str__(self):
|
||||
return 'version: {}\npayload: {}\nprefix: {}'.format(self.version, self.payload, self.prefix)
|
||||
|
||||
def legacy_address(self):
|
||||
version_int = Address._address_type('legacy', self.version)[1]
|
||||
return b58encode_check(Address.code_list_to_string([version_int] + self.payload))
|
||||
|
||||
def cash_address(self):
|
||||
version_int = Address._address_type('cash', self.version)[1]
|
||||
payload = [version_int] + self.payload
|
||||
payload = convertbits(payload, 8, 5)
|
||||
checksum = calculate_checksum(self.prefix, payload)
|
||||
return self.prefix + ':' + b32encode(payload + checksum)
|
||||
|
||||
@staticmethod
|
||||
def code_list_to_string(code_list):
|
||||
if sys.version_info > (3, 0):
|
||||
output = bytes()
|
||||
for code in code_list:
|
||||
output += bytes([code])
|
||||
else:
|
||||
output = ''
|
||||
for code in code_list:
|
||||
output += chr(code)
|
||||
return output
|
||||
|
||||
@staticmethod
|
||||
def _address_type(address_type, version):
|
||||
for mapping in Address.VERSION_MAP[address_type]:
|
||||
if mapping[0] == version or mapping[1] == version:
|
||||
return mapping
|
||||
raise InvalidAddress('Could not determine address version')
|
||||
|
||||
@staticmethod
|
||||
def from_string(address_string):
|
||||
try:
|
||||
address_string = str(address_string)
|
||||
except Exception:
|
||||
raise InvalidAddress('Expected string as input')
|
||||
if ':' not in address_string:
|
||||
return Address._legacy_string(address_string)
|
||||
else:
|
||||
return Address._cash_string(address_string)
|
||||
|
||||
@staticmethod
|
||||
def _legacy_string(address_string):
|
||||
try:
|
||||
decoded = bytearray(b58decode_check(address_string))
|
||||
except ValueError:
|
||||
raise InvalidAddress('Could not decode legacy address')
|
||||
version = Address._address_type('legacy', decoded[0])[0]
|
||||
payload = list()
|
||||
for letter in decoded[1:]:
|
||||
payload.append(letter)
|
||||
return Address(version, payload)
|
||||
|
||||
@staticmethod
|
||||
def _cash_string(address_string):
|
||||
if address_string.upper() != address_string and address_string.lower() != address_string:
|
||||
raise InvalidAddress('Cash address contains uppercase and lowercase characters')
|
||||
address_string = address_string.lower()
|
||||
colon_count = address_string.count(':')
|
||||
if colon_count == 0:
|
||||
address_string = Address.MAINNET_PREFIX + ':' + address_string
|
||||
elif colon_count > 1:
|
||||
raise InvalidAddress('Cash address contains more than one colon character')
|
||||
prefix, base32string = address_string.split(':')
|
||||
decoded = b32decode(base32string)
|
||||
if not verify_checksum(prefix, decoded):
|
||||
raise InvalidAddress('Bad cash address checksum')
|
||||
converted = convertbits(decoded, 5, 8)
|
||||
version = Address._address_type('cash', converted[0])[0]
|
||||
if prefix == Address.TESTNET_PREFIX:
|
||||
version += '-TESTNET'
|
||||
payload = converted[1:-6]
|
||||
return Address(version, payload, prefix)
|
||||
|
||||
|
||||
def to_cash_address(address):
|
||||
return Address.from_string(address).cash_address()
|
||||
|
||||
|
||||
def to_legacy_address(address):
|
||||
return Address.from_string(address).legacy_address()
|
||||
|
||||
|
||||
def is_valid(address):
|
||||
try:
|
||||
Address.from_string(address)
|
||||
return True
|
||||
except InvalidAddress:
|
||||
return False
|
||||
|
||||
#########################################################################
|
||||
## monero
|
||||
#########################################################################
|
||||
|
||||
|
||||
__alphabet = [
|
||||
ord(s) for s in "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
]
|
||||
__b58base = 58
|
||||
__UINT64MAX = 2 ** 64
|
||||
__encodedBlockSizes = [0, 2, 3, 5, 6, 7, 9, 10, 11]
|
||||
__fullBlockSize = 8
|
||||
__fullEncodedBlockSize = 11
|
||||
|
||||
|
||||
def _hexToBin(hex_):
|
||||
if len(hex_) % 2 != 0:
|
||||
raise ValueError("Hex string has invalid length: %d" % len(hex_))
|
||||
return [int(hex_[i : i + 2], 16) for i in range(0, len(hex_), 2)]
|
||||
|
||||
|
||||
def _binToHex(bin_):
|
||||
return "".join("%02x" % int(b) for b in bin_)
|
||||
|
||||
|
||||
def _uint8be_to_64(data):
|
||||
if not (1 <= len(data) <= 8):
|
||||
raise ValueError("Invalid input length: %d" % len(data))
|
||||
|
||||
res = 0
|
||||
for b in data:
|
||||
res = res << 8 | b
|
||||
return res
|
||||
|
||||
|
||||
def _uint64_to_8be(num, size):
|
||||
if size < 1 or size > 8:
|
||||
raise ValueError("Invalid input length: %d" % size)
|
||||
res = [0] * size
|
||||
|
||||
twopow8 = 2 ** 8
|
||||
for i in range(size - 1, -1, -1):
|
||||
res[i] = num % twopow8
|
||||
num = num // twopow8
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def encode_block(data, buf, index):
|
||||
l_data = len(data)
|
||||
|
||||
if l_data < 1 or l_data > __fullEncodedBlockSize:
|
||||
raise ValueError("Invalid block length: %d" % l_data)
|
||||
|
||||
num = _uint8be_to_64(data)
|
||||
i = __encodedBlockSizes[l_data] - 1
|
||||
|
||||
while num > 0:
|
||||
remainder = num % __b58base
|
||||
num = num // __b58base
|
||||
buf[index + i] = __alphabet[remainder]
|
||||
i -= 1
|
||||
|
||||
return buf
|
||||
|
||||
|
||||
def encode(hex):
|
||||
"""Encode hexadecimal string as base58 (ex: encoding a Monero address)."""
|
||||
data = _hexToBin(hex)
|
||||
l_data = len(data)
|
||||
|
||||
if l_data == 0:
|
||||
return ""
|
||||
|
||||
full_block_count = l_data // __fullBlockSize
|
||||
last_block_size = l_data % __fullBlockSize
|
||||
res_size = (
|
||||
full_block_count * __fullEncodedBlockSize + __encodedBlockSizes[last_block_size]
|
||||
)
|
||||
|
||||
res = bytearray([__alphabet[0]] * res_size)
|
||||
|
||||
for i in range(full_block_count):
|
||||
res = encode_block(
|
||||
data[(i * __fullBlockSize) : (i * __fullBlockSize + __fullBlockSize)],
|
||||
res,
|
||||
i * __fullEncodedBlockSize,
|
||||
)
|
||||
|
||||
if last_block_size > 0:
|
||||
res = encode_block(
|
||||
data[
|
||||
(full_block_count * __fullBlockSize) : (
|
||||
full_block_count * __fullBlockSize + last_block_size
|
||||
)
|
||||
],
|
||||
res,
|
||||
full_block_count * __fullEncodedBlockSize,
|
||||
)
|
||||
|
||||
return bytes(res).decode("ascii")
|
||||
|
||||
|
||||
def decode_block(data, buf, index):
|
||||
l_data = len(data)
|
||||
|
||||
if l_data < 1 or l_data > __fullEncodedBlockSize:
|
||||
raise ValueError("Invalid block length: %d" % l_data)
|
||||
|
||||
res_size = __encodedBlockSizes.index(l_data)
|
||||
if res_size <= 0:
|
||||
raise ValueError("Invalid block size: %d" % res_size)
|
||||
|
||||
res_num = 0
|
||||
order = 1
|
||||
for i in range(l_data - 1, -1, -1):
|
||||
digit = __alphabet.index(data[i])
|
||||
if digit < 0:
|
||||
raise ValueError("Invalid symbol: %s" % data[i])
|
||||
|
||||
product = order * digit + res_num
|
||||
if product > __UINT64MAX:
|
||||
raise ValueError(
|
||||
"Overflow: %d * %d + %d = %d" % (order, digit, res_num, product)
|
||||
)
|
||||
|
||||
res_num = product
|
||||
order = order * __b58base
|
||||
|
||||
if res_size < __fullBlockSize and 2 ** (8 * res_size) <= res_num:
|
||||
raise ValueError("Overflow: %d doesn't fit in %d bit(s)" % (res_num, res_size))
|
||||
|
||||
tmp_buf = _uint64_to_8be(res_num, res_size)
|
||||
buf[index : index + len(tmp_buf)] = tmp_buf
|
||||
|
||||
return buf
|
||||
|
||||
|
||||
def decode(enc):
|
||||
"""Decode a base58 string (ex: a Monero address) into hexidecimal form."""
|
||||
enc = bytearray(enc, encoding="ascii")
|
||||
l_enc = len(enc)
|
||||
|
||||
if l_enc == 0:
|
||||
return ""
|
||||
|
||||
full_block_count = l_enc // __fullEncodedBlockSize
|
||||
last_block_size = l_enc % __fullEncodedBlockSize
|
||||
try:
|
||||
last_block_decoded_size = __encodedBlockSizes.index(last_block_size)
|
||||
except ValueError:
|
||||
raise ValueError("Invalid encoded length: %d" % l_enc)
|
||||
|
||||
data_size = full_block_count * __fullBlockSize + last_block_decoded_size
|
||||
|
||||
data = bytearray(data_size)
|
||||
for i in range(full_block_count):
|
||||
data = decode_block(
|
||||
enc[
|
||||
(i * __fullEncodedBlockSize) : (
|
||||
i * __fullEncodedBlockSize + __fullEncodedBlockSize
|
||||
)
|
||||
],
|
||||
data,
|
||||
i * __fullBlockSize,
|
||||
)
|
||||
|
||||
if last_block_size > 0:
|
||||
data = decode_block(
|
||||
enc[
|
||||
(full_block_count * __fullEncodedBlockSize) : (
|
||||
full_block_count * __fullEncodedBlockSize + last_block_size
|
||||
)
|
||||
],
|
||||
data,
|
||||
full_block_count * __fullBlockSize,
|
||||
)
|
||||
|
||||
return _binToHex(data)
|
||||
|
||||
#########################################################################
|
||||
#########################################################################
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-04-29 14:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0004_vendorsdata_vendoruuid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorIsActive',
|
||||
field=models.BooleanField(default=1),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 4.2 on 2023-04-29 14:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0005_alter_vendorsdata_vendorisactive'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorDeleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorPaidNotification',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorPayWindow',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorSkipScreen',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.2 on 2023-04-29 17:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0006_alter_vendorsdata_vendordeleted_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorUpdated',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorAddDelete',
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-05-02 12:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0007_vendorsdata_vendorupdated_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='vendorWebAddr',
|
||||
field=models.URLField(max_length=250),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2 on 2023-05-02 15:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('base', '0008_alter_vendorsdata_vendorwebaddr'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='vendorsdata',
|
||||
name='id',
|
||||
field=models.BigAutoField(primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
|
@ -1,30 +1,74 @@
|
|||
from django.db import models
|
||||
from users.models import User
|
||||
from django.contrib import admin
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class CryptoCoins(models.Model):
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
coinName = models.CharField(max_length=250)
|
||||
coinSymbol = models.CharField(max_length=10)
|
||||
coinIsActive = models.BooleanField(default=True)
|
||||
coinAddDate = models.DateTimeField(auto_now_add=True)
|
||||
coinUpdated = models.DateTimeField(auto_now=True)
|
||||
coinDelete = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||
coinDeleted = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Crypto Coin"
|
||||
verbose_name_plural = "Crypto Coins"
|
||||
ordering = ["-id"]
|
||||
|
||||
class VendorsData(models.Model):
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
|
||||
vendorUUID = models.UUIDField()
|
||||
vendor = models.CharField(max_length=8, unique=True)
|
||||
vendorSecretKey = models.UUIDField()
|
||||
vendorSecretKey = models.CharField(max_length=250)
|
||||
vendorCoverAmount = models.IntegerField(default=0)
|
||||
vendorPaidNotification = models.IntegerField(default=1)
|
||||
vendorSkipScreen = models.IntegerField(default=0)
|
||||
vendorPayWindow = models.IntegerField(default=0)
|
||||
vendorNetworkFee = models.IntegerField(default=0)
|
||||
vendorPaidNotification = models.BooleanField(default=True)
|
||||
vendorSkipScreen = models.BooleanField(default=False)
|
||||
vendorPayWindow = models.BooleanField(default=True)
|
||||
vendorWebName = models.CharField(max_length=250)
|
||||
vendorWebAddr = models.CharField(max_length=250)
|
||||
vendorWebAddr = models.URLField(max_length=250)
|
||||
vendorAddDate = models.DateTimeField(auto_now_add=True)
|
||||
vendorDeleted = models.IntegerField(default=0)
|
||||
vendorAddDelete = models.DateTimeField(auto_now=True)
|
||||
vendorIsActive = models.IntegerField(default=1)
|
||||
vendorDeleted = models.BooleanField(default=False)
|
||||
vendorUpdated = models.DateTimeField(auto_now=True)
|
||||
vendorAddDelete = models.DateTimeField(auto_now_add=True)
|
||||
vendorIsActive = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.vendor
|
||||
|
||||
def __repr__(self):
|
||||
return self._repr()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Vendor"
|
||||
verbose_name_plural = "Vendors"
|
||||
ordering = ["-id"]
|
||||
|
||||
|
||||
|
||||
class VendorsAddresses(models.Model):
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
vendor = models.ForeignKey(VendorsData, default=None, on_delete=models.CASCADE)
|
||||
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
|
||||
vendorUUID = models.UUIDField()
|
||||
coin = models.CharField(max_length=10)
|
||||
address = models.CharField(max_length=250)
|
||||
addrAddDate = models.DateTimeField(auto_now_add=True)
|
||||
addrDeleted = models.BooleanField(default=False)
|
||||
addrUpdated = models.DateTimeField(auto_now=True)
|
||||
addrIsActive = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.address
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Address"
|
||||
verbose_name_plural = "Addresses"
|
||||
ordering = ["-addrAddDate"]
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7,321 +7,349 @@
|
|||
<!-- ============================================================== -->
|
||||
<!-- Main content -->
|
||||
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
|
||||
<!-- Header -->
|
||||
<header class="bg-surface-primary border-bottom pt-6">
|
||||
<div class="container-fluid">
|
||||
<div class="mb-npx">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-sm-6 col-12 mb-4 mb-sm-0">
|
||||
<!-- Title -->
|
||||
<h1 class="h2 mb-0 ls-tight"> Dashboard </h1>
|
||||
<hr class="navbar-divider my-1 opacity-20">
|
||||
<p> Hello "user", welcome! Here's what's happening with your store today.</p>
|
||||
</div>
|
||||
<!-- Actions -->
|
||||
<hr class="navbar-divider my-1 opacity-20">
|
||||
</div>
|
||||
<!-- Nav -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main -->
|
||||
<main class="py-6 bg-surface-secondary">
|
||||
<div class="container-fluid">
|
||||
<!-- Card stats -->
|
||||
<div class="row g-6 mb-6">
|
||||
<div class="col-xl-3 col-sm-6 col-12">
|
||||
<div class="card shadow border-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Earnings</span>
|
||||
<span class="h3 font-bold mb-0">$750.90</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="icon icon-shape bg-tertiary text-white text-lg rounded-circle">
|
||||
<i class="bi bi-credit-card"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 mb-0 text-sm">
|
||||
<span class="badge badge-pill bg-soft-success text-success me-2">
|
||||
<i class="bi bi-arrow-up me-1"></i>13%
|
||||
</span>
|
||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-sm-6 col-12">
|
||||
<div class="card shadow border-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Balance</span>
|
||||
<span class="h3 font-bold mb-0">215</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="icon icon-shape bg-primary text-white text-lg rounded-circle">
|
||||
<i class="bi bi-people"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 mb-0 text-sm">
|
||||
<span class="badge badge-pill bg-soft-success text-success me-2">
|
||||
<i class="bi bi-arrow-up me-1"></i>30%
|
||||
</span>
|
||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-sm-6 col-12">
|
||||
<div class="card shadow border-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Customers</span>
|
||||
<span class="h3 font-bold mb-0">1.400</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="icon icon-shape bg-info text-white text-lg rounded-circle">
|
||||
<i class="bi bi-clock-history"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 mb-0 text-sm">
|
||||
<span class="badge badge-pill bg-soft-danger text-danger me-2">
|
||||
<i class="bi bi-arrow-down me-1"></i>-5%
|
||||
</span>
|
||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-sm-6 col-12">
|
||||
<div class="card shadow border-0">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Invoices</span>
|
||||
<span class="h3 font-bold mb-0">150</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="icon icon-shape bg-warning text-white text-lg rounded-circle">
|
||||
<i class="bi bi-minecart-loaded"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 mb-0 text-sm">
|
||||
<span class="badge badge-pill bg-soft-success text-success me-2">
|
||||
<i class="bi bi-arrow-up me-1"></i>10%
|
||||
</span>
|
||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-fill overflow-y-lg-auto scrollbar bg-surface-primary rounded-top-4 rounded-top-start-lg-4 rounded-top-end-lg-0 border-top border-lg shadow">
|
||||
<main class="container-fluid px-3 py-5 p-lg-6 p-xxl-10">
|
||||
<div class="mb-6 mb-xl-10">
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col">
|
||||
<h1 class="h2 ls-tight">Dashboard</h1>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="card shadow border-0 mb-7">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Last 5 paid invoices</h5>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 g-xxl-6">
|
||||
<div class="col-xxl-12">
|
||||
<div class="vstack gap-3 gap-md-6">
|
||||
<div class="row g-3">
|
||||
<div class="col-md col-sm-6">
|
||||
<div class="card bg-success bg-opacity-10 border-success border-opacity-40">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img class="w-5 flex-none" src="{% static '/img/crypto/color/usdt.svg' %}" alt="..." />
|
||||
<h6>USDT</h6>
|
||||
</div>
|
||||
<div class="text-xs mt-4">
|
||||
Total balance
|
||||
</div>
|
||||
<div class="text-sm font-semibold">
|
||||
75.800,00 USDT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md col-sm-6">
|
||||
<div class="card border-primary-hover">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img class="w-5 flex-none" src="{% static '/img/crypto/color/btc.svg' %}" alt="..." />
|
||||
<a href="/pages/exchange-details.html" class="h6 stretched-link">BTC</a>
|
||||
</div>
|
||||
<div class="text-sm font-semibold mt-3">
|
||||
3.2893 USDT
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
|
||||
<span class="badge badge-xs rounded-pill text-bg-success">
|
||||
<i class="bi bi-arrow-up-right"></i>
|
||||
</span>
|
||||
<span>+13.7%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md col-sm-6">
|
||||
<div class="card border-primary-hover">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img class="w-5 flex-none" src="{% static '/img/crypto/color/ada.svg' %}" alt="..." />
|
||||
<a href="/pages/exchange-details.html" class="h6 stretched-link">ADA</a>
|
||||
</div>
|
||||
<div class="text-sm font-semibold mt-3">
|
||||
10.745,49 ADA
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
|
||||
<span class="badge badge-xs rounded-pill text-bg-danger">
|
||||
<i class="bi bi-arrow-up-right"></i>
|
||||
</span>
|
||||
<span>-3.2%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md col-sm-6">
|
||||
<div class="card border-primary-hover">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<img class="w-5 flex-none" src="{% static '/img/crypto/color/eos.svg' %}" alt="..." />
|
||||
<a href="/pages/exchange-details.html" class="h6 stretched-link">EOS</a>
|
||||
</div>
|
||||
<div class="text-sm font-semibold mt-3">
|
||||
7.890,00 EOS
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
|
||||
<span class="badge badge-xs rounded-pill text-bg-danger">
|
||||
<i class="bi bi-arrow-up-right"></i>
|
||||
</span>
|
||||
<span>-2.2%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 d-none d-md-block">
|
||||
<div class="card h-md-full d-flex flex-column align-items-center justify-content-center py-4 bg-light bg-opacity-80 bg-opacity-100-hover">
|
||||
<a href="#cryptoModal" class="stretched-link text-dark" data-bs-toggle="modal">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-nowrap">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Company</th>
|
||||
<th scope="col">Offer</th>
|
||||
<th scope="col">Meeting</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<img alt="..." src="https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Robert Fox
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Feb 15, 2021
|
||||
</td>
|
||||
<td>
|
||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Dribbble
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
$3.500
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-lg badge-dot">
|
||||
<i class="bg-success"></i>Scheduled
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img alt="..." src="https://images.unsplash.com/photo-1610271340738-726e199f0258?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Darlene Robertson
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Apr 15, 2021
|
||||
</td>
|
||||
<td>
|
||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-2.png" class="avatar avatar-xs rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Netguru
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
$2.750
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-lg badge-dot">
|
||||
<i class="bg-warning"></i>Postponed
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img alt="..." src="https://images.unsplash.com/photo-1610878722345-79c5eaf6a48c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Theresa Webb
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Mar 20, 2021
|
||||
</td>
|
||||
<td>
|
||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-3.png" class="avatar avatar-xs rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Figma
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
$4.200
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-lg badge-dot">
|
||||
<i class="bg-success"></i>Scheduled
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img alt="..." src="https://images.unsplash.com/photo-1612422656768-d5e4ec31fac0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Kristin Watson
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Feb 15, 2021
|
||||
</td>
|
||||
<td>
|
||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-4.png" class="avatar avatar-xs rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Mailchimp
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
$3.500
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-lg badge-dot">
|
||||
<i class="bg-dark"></i>Not discussed
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img alt="..." src="https://images.unsplash.com/photo-1608976328267-e673d3ec06ce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Cody Fisher
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Apr 10, 2021
|
||||
</td>
|
||||
<td>
|
||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-5.png" class="avatar avatar-xs rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
Webpixels
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
$1.500
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-lg badge-dot">
|
||||
<i class="bg-danger"></i>Canceled
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer border-0 py-5">
|
||||
<span class="text-muted text-sm">#</span>
|
||||
<div class="card">
|
||||
<div class="card-body pb-0">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5>Transaction History</h5>
|
||||
</div>
|
||||
<div class="hstack align-items-center">
|
||||
<a href="#" class="text-muted">
|
||||
<i class="bi bi-arrow-repeat"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span class="d-block text-heading text-sm font-semibold">
|
||||
Bitcoin
|
||||
</span>
|
||||
<span class="d-none d-sm-block text-muted text-xs">
|
||||
2 days ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-sm">
|
||||
0xd029384sd343fd...eq23
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<span class="badge bg-light text-warning rounded-pill">
|
||||
Pending
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-heading text-sm font-bold">
|
||||
+0.2948 BTC
|
||||
</span>
|
||||
<span class="d-block text-muted text-xs">
|
||||
+$10,930.90
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span class="d-block text-heading text-sm font-semibold">
|
||||
Cardano
|
||||
</span>
|
||||
<span class="d-none d-sm-block text-muted text-xs">
|
||||
2 days ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-sm">
|
||||
0xd029384sd343fd...eq23
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<span class="badge bg-light text-danger rounded-pill">
|
||||
Canceled
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-heading text-sm font-bold">
|
||||
+0.2948 BTC
|
||||
</span>
|
||||
<span class="d-block text-muted text-xs">
|
||||
+$10,930.90
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span class="d-block text-heading text-sm font-semibold">
|
||||
Binance
|
||||
</span>
|
||||
<span class="d-none d-sm-block text-muted text-xs">
|
||||
2 days ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-sm">
|
||||
0xd029384sd343fd...eq23
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<span class="badge bg-light text-success rounded-pill">
|
||||
Successful
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-heading text-sm font-bold">
|
||||
+0.2948 BTC
|
||||
</span>
|
||||
<span class="d-block text-muted text-xs">
|
||||
+$10,930.90
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span class="d-block text-heading text-sm font-semibold">
|
||||
Bitcoin
|
||||
</span>
|
||||
<span class="d-none d-sm-block text-muted text-xs">
|
||||
2 days ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-sm">
|
||||
0xd029384sd343fd...eq23
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<span class="badge bg-light text-warning rounded-pill">
|
||||
Pending
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-heading text-sm font-bold">
|
||||
+0.2948 BTC
|
||||
</span>
|
||||
<span class="d-block text-muted text-xs">
|
||||
+$10,930.90
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span class="d-block text-heading text-sm font-semibold">
|
||||
Bitcoin
|
||||
</span>
|
||||
<span class="d-none d-sm-block text-muted text-xs">
|
||||
2 days ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-sm">
|
||||
0xd029384sd343fd...eq23
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<span class="badge bg-light text-danger rounded-pill">
|
||||
Canceled
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-heading text-sm font-bold">
|
||||
+0.2948 BTC
|
||||
</span>
|
||||
<span class="d-block text-muted text-xs">
|
||||
+$10,930.90
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span class="d-block text-heading text-sm font-semibold">
|
||||
Bitcoin
|
||||
</span>
|
||||
<span class="d-none d-sm-block text-muted text-xs">
|
||||
2 days ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-sm">
|
||||
0xd029384sd343fd...eq23
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<span class="badge bg-light text-success rounded-pill">
|
||||
Successful
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-heading text-sm font-bold">
|
||||
+0.2948 BTC
|
||||
</span>
|
||||
<span class="d-block text-muted text-xs">
|
||||
+$10,930.90
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||
<i class="bi bi-send-fill"></i>
|
||||
</div>
|
||||
<div class="">
|
||||
<span class="d-block text-heading text-sm font-semibold">
|
||||
Bitcoin
|
||||
</span>
|
||||
<span class="d-none d-sm-block text-muted text-xs">
|
||||
2 days ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-md-block text-sm">
|
||||
0xd029384sd343fd...eq23
|
||||
</div>
|
||||
<div class="d-none d-md-block">
|
||||
<span class="badge bg-light text-success rounded-pill">
|
||||
Successful
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-heading text-sm font-bold">
|
||||
+0.2948 BTC
|
||||
</span>
|
||||
<span class="d-block text-muted text-xs">
|
||||
+$10,930.90
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dashboard -->
|
||||
<!-- End Page-content -->
|
||||
<div>
|
||||
{% block footer %}
|
||||
{% include "partials/footer.html" %}
|
||||
{% endblock footer %}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<!-- end main content-->
|
||||
{% endblock content %}
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "partials/base.html" %}
|
||||
{% load static %}
|
||||
{% block title %}Dashboard{% endblock title %}
|
||||
{% block title %}Vendor Page{% endblock title %}
|
||||
{% block content %}
|
||||
<!-- ============================================================== -->
|
||||
<!-- Start right Content here -->
|
||||
|
@ -8,24 +8,7 @@
|
|||
<!-- Main content -->
|
||||
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
|
||||
<!-- Header -->
|
||||
<header class="bg-surface-primary border-bottom pt-6">
|
||||
<div class="container-fluid">
|
||||
<div class="mb-npx">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-sm-6 col-12 mb-4 mb-sm-0">
|
||||
<!-- Title -->
|
||||
<h1 class="h2 mb-0 ls-tight"> Vendor Page </h1>
|
||||
<hr class="navbar-divider my-1 opacity-20">
|
||||
|
||||
</div>
|
||||
<!-- Actions -->
|
||||
<hr class="navbar-divider my-1 opacity-20">
|
||||
</div>
|
||||
<!-- Nav -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main -->
|
||||
<main class="py-6 bg-surface-secondary">
|
||||
<div class="container-fluid">
|
||||
|
@ -39,9 +22,9 @@
|
|||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col">VendorID</th>
|
||||
<th scope="col">Creation Date</th>
|
||||
<th scope="col">Website Name</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col">Created</th>
|
||||
<th scope="col">Updated</th>
|
||||
<th scope="col">Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
@ -50,22 +33,18 @@
|
|||
|
||||
<tr>
|
||||
<td>
|
||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
{{vendor.vendor}}
|
||||
</a>
|
||||
{{vendor.vendor}}
|
||||
</td>
|
||||
<td>
|
||||
{{vendor.vendorAddDate}}
|
||||
</td>
|
||||
<td>
|
||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
|
||||
<a class="text-heading font-semibold" href="#">
|
||||
{{vendor.vendorWebName}}
|
||||
</a>
|
||||
{{vendor.vendorWebName|truncatechars:20}}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
{{vendor.vendorAddDate}}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{vendor.vendorUpdated|timesince}} ago
|
||||
</td>
|
||||
<td>
|
||||
{% if vendor.vendorIsActive == 0 %}
|
||||
|
@ -79,8 +58,7 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a href="{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Edit</a>
|
||||
<a href="addresses/{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Addresses</a>
|
||||
<a href="{% url 'edit-vendor' vendor.vendorUUID %}" class="btn d-inline-flex btn-sm btn-neutral text-neutral"><span><i class="bi bi-gear me-1"></i></span> <span class="d-none d-sm-block me-2">Manage</span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -90,7 +68,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<div class="card-footer border-0 py-5">
|
||||
<span class="text-muted text-sm">#</span>
|
||||
<div class="text-center"> <a href="{% url 'create-vendor' %}" class="btn btn-sm btn-primary">Create</a> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -99,13 +77,5 @@
|
|||
</div>
|
||||
<!-- Dashboard -->
|
||||
<!-- End Page-content -->
|
||||
<div>
|
||||
{% block footer %}
|
||||
{% include "partials/footer.html" %}
|
||||
{% endblock footer %}
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- end main content-->
|
||||
{% endblock content %}
|
|
@ -0,0 +1,184 @@
|
|||
{% extends "partials/base.html" %}
|
||||
{% load static %}
|
||||
{% block title %}Vendor Edit Page{% endblock title %}
|
||||
{% block content %}
|
||||
|
||||
|
||||
<!-- ============================================================== -->
|
||||
<!-- Start right Content here -->
|
||||
<!-- ============================================================== -->
|
||||
<!-- Main content -->
|
||||
|
||||
|
||||
<div class="flex-fill overflow-y-lg-auto scrollbar bg-surface-primary rounded-top-4 rounded-top-start-lg-4 rounded-top-end-lg-0 border-top border-lg shadow">
|
||||
<main class="container-fluid px-3 py-5 p-lg-6 p-xxl-10">
|
||||
<header class="border-bottom mb-10">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-sm-6 col-12">
|
||||
<h1 class="h2 ls-tight">
|
||||
{% block vendor_title %} {{vendor_title}} {% endblock vendor_title %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="nav nav-tabs overflow-x border-0 mt-4">
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'edit-vendor' vendors.vendorUUID %}" class="nav-link">Options</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{% url 'add-address' vendors.vendorUUID %}" class="nav-link">Addresses</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/account-billing.html" class="nav-link">Delete</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</header>
|
||||
|
||||
|
||||
<form method="POST" action="">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form.non_field_errors }}
|
||||
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-2">
|
||||
<label for="{{ form.vendorWebName.id_for_label}}" class="form-label">Website Name <span><i class="bi bi-patch-question-fill text-danger" title="Shown on invoice page"></i></span></label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xl-5">
|
||||
<div class="">
|
||||
<div class="input-group position-relative">
|
||||
<span class="input-group-text"><i class="bi bi-three-dots"></i></span>
|
||||
<input id="vendorWebName" name="vendorWebName" type="text" class="form-control" {% if not vendors.vendorWebName %} placeholder="Website Name" {% else %} value="{{vendors.vendorWebName}}" {% endif %} aria-label="vendorWebName">
|
||||
</div>
|
||||
<span class="mt-2 valid-feedback">Looks good!</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="my-6" />
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-2">
|
||||
<label for="{{ form.vendorWebAddr.id_for_label}}" class="form-label">Website URL <span><i class="bi bi-patch-question-fill text-danger" title="Shown on invoice page"></i></span></label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xl-5">
|
||||
<div class="">
|
||||
<div class="input-group position-relative">
|
||||
<span class="input-group-text"><i class="bi bi-window"></i></span>
|
||||
<input id="vendorWebAddr" name="vendorWebAddr" type="text" class="form-control" {% if not vendors.vendorWebAddr %} placeholder="Website URL" {% else %} value="{{vendors.vendorWebAddr}}" {% endif %} aria-label="vendorWebAddr">
|
||||
</div>
|
||||
{% if form.vendorWebAddr.errors %}
|
||||
{% for error in form.vendorWebAddr.errors %}
|
||||
<span class="mt-2 valid-feedback">{{ error|escape }}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="my-6" />
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-2">
|
||||
<label for="{{ form.vendorNetworkFee.id_for_label}}" class="form-label">Network Fee % <span><i class="bi bi-patch-question-fill text-danger" title="Max percentage "></i></span> </label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xl-2">
|
||||
<select id="vendorNetworkFee" name="vendorNetworkFee" class="form-select" aria-describedby="vendorNetworkFeeHelpBlock">
|
||||
<option value="0" {% if vendors.vendorNetworkFee == 0 %} selected="selected" {% endif %}>0%</option>
|
||||
<option value="5" {% if vendors.vendorNetworkFee == 5 %} selected="selected" {% endif %}>5%</option>
|
||||
<option value="10" {% if vendors.vendorNetworkFee == 10 %} selected="selected" {% endif %}>10%</option>
|
||||
</select>
|
||||
{% if form.vendorNetworkFee.errors %}
|
||||
{% for error in form.vendorNetworkFee.errors %}
|
||||
<span id="vendorNetworkFeeHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<p class="my-6" />
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-2">
|
||||
<label for="vendorPaidNotification" class="form-label">Notifications <span><i class="bi bi-patch-question-fill text-danger" title="Get notified by email for each payment received?"></i></span></label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xl-2">
|
||||
<select id="vendorPaidNotification" name="vendorPaidNotification" class="form-select" aria-describedby="vendorPaidNotificationHelpBlock">
|
||||
<option value="true" {% if vendors.vendorPaidNotification == True %} selected="selected" {% endif %}>Yes</option>
|
||||
<option value="false" {% if vendors.vendorPaidNotification == False %} selected="selected" {% endif %}>No</option>
|
||||
</select>
|
||||
</div>
|
||||
{% if form.vendorPaidNotification.errors %}
|
||||
{% for error in form.vendorPaidNotification.errors %}
|
||||
<span id="vendorPaidNotificationHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="my-6" />
|
||||
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-2">
|
||||
<label for="vendorCoverAmount" class="form-label">Cover Amount <span><i class="bi bi-patch-question-fill text-danger" title="Amount covered by you in case the buyer sends less than required"></i></span> </label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xl-2">
|
||||
<select id="vendorCoverAmount" name="vendorCoverAmount" class="form-select" aria-describedby="vendorCoverAmountHelpBlock">
|
||||
<option value="0" {% if vendors.vendorCoverAmount == 0 %} selected="selected" {% endif %}>0%</option>
|
||||
<option value="1" {% if vendors.vendorCoverAmount == 1 %} selected="selected" {% endif %}>1%</option>
|
||||
<option value="2" {% if vendors.vendorCoverAmount == 2 %} selected="selected" {% endif %}>2%</option>
|
||||
<option value="3" {% if vendors.vendorCoverAmount == 3 %} selected="selected" {% endif %}>3%</option>
|
||||
<option value="4" {% if vendors.vendorCoverAmount == 4 %} selected="selected" {% endif %}>4%</option>
|
||||
<option value="5" {% if vendors.vendorCoverAmount == 5 %} selected="selected" {% endif %}>5%</option>
|
||||
<option value="6" {% if vendors.vendorCoverAmount == 6 %} selected="selected" {% endif %}>6%</option>
|
||||
<option value="7" {% if vendors.vendorCoverAmount == 7 %} selected="selected" {% endif %}>7%</option>
|
||||
<option value="8" {% if vendors.vendorCoverAmount == 8 %} selected="selected" {% endif %}>8%</option>
|
||||
<option value="9" {% if vendors.vendorCoverAmount == 9 %} selected="selected" {% endif %}>9%</option>
|
||||
<option value="10" {% if vendors.vendorCoverAmount == 10 %} selected="selected" {% endif %}>10%</option>
|
||||
</select>
|
||||
{% if form.vendorCoverAmount.errors %}
|
||||
{% for error in form.vendorCoverAmount.errors %}
|
||||
<span id="vendorCoverAmountHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<p class="my-6" />
|
||||
<div class="form-group row">
|
||||
<div class="col-md-2">
|
||||
<label for="vendorPayWindow" class="form-label">Payment <span><i class="bi bi-patch-question-fill text-danger" title="When should we send you your payments?"></i></span></label>
|
||||
</div>
|
||||
<div class="col-md-8 col-xl-2">
|
||||
<select id="vendorPayWindow" name="vendorPayWindow" class="form-select" aria-describedby="vendorPayWindowHelpBlock">
|
||||
<option value="true" {% if vendors.vendorPayWindow == 1 %} selected="selected" {% endif %}>Hourly</option>
|
||||
<option value="false" {% if vendors.vendorPayWindow == 0 %} selected="selected" {% endif %}>Daily</option>
|
||||
</select>
|
||||
{% if form.vendorPayWindow.errors %}
|
||||
{% for error in form.vendorPayWindow.errors %}
|
||||
<span id="vendorPayWindowHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-6" />
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="offset-4 col-8">
|
||||
<input name="submit" type="submit" class="btn btn-primary" value="Submit" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
<ul class="messages text-center">
|
||||
{% for message in messages %}
|
||||
<p{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</p>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dashboard -->
|
||||
<!-- End Page-content -->
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- end main content-->
|
||||
{% endblock content %}
|
|
@ -5,7 +5,14 @@ from django.urls import path
|
|||
urlpatterns = [
|
||||
path('login/', views.LoginPage, name="login"),
|
||||
path('logout/', views.LogoutPage, name="logout"),
|
||||
|
||||
|
||||
path('', views.index, name="dashboard"),
|
||||
path('vendor/', views.VendorPage, name="vendor"),
|
||||
path('vendor/edit/<str:vdr>/', views.VendorEditPage, name="edit-vendor"),
|
||||
path('vendor/create', views.VendorCreatePage, name="create-vendor"),
|
||||
path('vendor/address/<str:vdr>/', views.VendorAddrPage, name="add-address"),
|
||||
|
||||
|
||||
#path('vendor-success', views.VendorSuccess, name="vendor-success")
|
||||
]
|
150
base/views.py
150
base/views.py
|
@ -1,13 +1,21 @@
|
|||
from django.shortcuts import render, redirect
|
||||
from django.contrib import messages
|
||||
from users.models import User
|
||||
from .models import VendorsData
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db.models import Q
|
||||
from django_otp.decorators import otp_required
|
||||
#
|
||||
from users.models import User
|
||||
from .models import VendorsData, CryptoCoins, VendorsAddresses
|
||||
from .forms import VendorEditForm, VendorAddrAddForm
|
||||
from .functions import vendor_generator, checksumCheck
|
||||
import uuid, hashlib
|
||||
|
||||
# Create your views here.
|
||||
@login_required(login_url='login')
|
||||
#@otp_required()
|
||||
def index(request):
|
||||
context = {}
|
||||
return render(request, 'base/dashboard.html', context)
|
||||
|
@ -33,7 +41,7 @@ def LoginPage(request):
|
|||
|
||||
|
||||
context = {}
|
||||
return render(request, 'base/login.html', context)
|
||||
return render(request, 'base/loogin2.html', context)
|
||||
|
||||
def LogoutPage(request):
|
||||
logout(request)
|
||||
|
@ -41,6 +49,136 @@ def LogoutPage(request):
|
|||
|
||||
@login_required(login_url='login')
|
||||
def VendorPage(request):
|
||||
vendors = VendorsData.objects.filter(vendorid_id=request.user.id)
|
||||
vendors = VendorsData.objects.filter(Q(vendorid_id=request.user.id) & Q(vendorDeleted=False))
|
||||
context = {'vendors': vendors}
|
||||
return render(request, 'base/vendor.html', context)
|
||||
return render(request, 'base/vendor.html', context)
|
||||
|
||||
@login_required(login_url='login')
|
||||
#@otp_required(login_url='two_factor:login')
|
||||
def VendorEditPage(request, vdr ):
|
||||
try:
|
||||
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
|
||||
form = VendorEditForm()
|
||||
vendor_title = "Edit Vendor"
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('submit') == 'Submit':
|
||||
try:
|
||||
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
|
||||
form = VendorEditForm(request.POST, instance=vendors)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, "Vendor information saved")
|
||||
return redirect('vendor')
|
||||
else:
|
||||
messages.error(request, "Something wrong happened, try again.")
|
||||
except Exception as e:
|
||||
messages.error(request, "Something wrong happened, try again.")
|
||||
context = {'vendors': vendors, 'form': form, 'vendor_title': vendor_title}
|
||||
return render(request, 'base/vendorEdit.html', context)
|
||||
except Exception as e:
|
||||
messages.error(request, e)
|
||||
return redirect('vendor')
|
||||
|
||||
@login_required(login_url='login')
|
||||
def VendorCreatePage(request):
|
||||
vendor_title = "Create Vendor"
|
||||
context = {'vendor_title': vendor_title}
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
try:
|
||||
vendor_accounts = len(VendorsData.objects.filter(vendorid_id=request.user.id))
|
||||
if vendor_accounts > request.user.vendornr:
|
||||
messages.error(request, "You have reached the maximum number of vendor accounts")
|
||||
return redirect('vendor')
|
||||
except: pass
|
||||
|
||||
form = VendorEditForm(request.POST)
|
||||
if form.is_valid():
|
||||
# Generate vendor
|
||||
new_vendor = form.save(commit=False)
|
||||
try:
|
||||
vdr = vendor_generator()
|
||||
vendor_check = VendorsData.objects.get(vendorUUID=vdr)
|
||||
while vendor_check is vdr:
|
||||
vdr = vendor_generator()
|
||||
vendor_check = VendorsData.objects.get(vendorUUID=vdr)
|
||||
except:
|
||||
pass
|
||||
|
||||
secretKey = str(uuid.uuid4())
|
||||
md5s = hashlib.md5(secretKey.encode()).hexdigest()
|
||||
new_vendor.vendor = vdr
|
||||
new_vendor.vendorSecretKey = md5s
|
||||
new_vendor.vendorSkipScreen = False
|
||||
new_vendor.vendorUUID = uuid.uuid4()
|
||||
new_vendor.vendorid_id = request.user.id
|
||||
# Save vendor
|
||||
try:
|
||||
new_vendor.save()
|
||||
vendor_title = "Congrats!"
|
||||
context = {'vendor_title': vendor_title, "vendor": new_vendor, 'secret': secretKey}
|
||||
return render(request, 'base/vendorSuccess.html', context)
|
||||
except Exception:
|
||||
messages.error(request, "Unable to create vendor") #
|
||||
else:
|
||||
messages.error(request, "The information submited is incomplete") #
|
||||
except Exception as e:
|
||||
messages.error(request, "Something wrong happened, try again.")
|
||||
return render(request, 'base/vendorCreate.html', context)
|
||||
|
||||
@login_required(login_url='login')
|
||||
def VendorAddrPage(request,vdr):
|
||||
try:
|
||||
coins = CryptoCoins.objects.filter(coinIsActive=True)
|
||||
try:
|
||||
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
|
||||
except:
|
||||
messages.error(request, "Vendor not found")
|
||||
return redirect('vendor')
|
||||
addresses = VendorsAddresses.objects.filter(Q(vendorUUID=vdr) & Q(vendorid_id=request.user.id))
|
||||
form = VendorAddrAddForm()
|
||||
vendor_title = "Add a crypto address"
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('submit') == 'Submit':
|
||||
try:
|
||||
if checksumCheck(request.POST.get('coin').lower(), request.POST.get('address')) == False:
|
||||
messages.error(request, "Invalid address")
|
||||
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
|
||||
return render(request, 'base/vendorAddr.html', context)
|
||||
except:
|
||||
# stop here, return nothing, notify us.
|
||||
return redirect('vendorAddr', vdr)
|
||||
##check if duplicate
|
||||
|
||||
|
||||
try:
|
||||
form = VendorAddrAddForm(request.POST)
|
||||
if form.is_valid():
|
||||
new_address = form.save(commit=False)
|
||||
new_address.vendorUUID = vdr
|
||||
new_address.addrDeleted = False
|
||||
new_address.addrIsActive = True
|
||||
new_address.vendor_id = vendors.id
|
||||
new_address.vendorid_id = request.user.id
|
||||
try:
|
||||
for obj in VendorsAddresses.objects.filter(Q(coin=request.POST.get('coin')) & Q(addrIsActive=True) & Q(vendorid_id=request.user.id)):
|
||||
obj.addrIsActive = False
|
||||
obj.save()
|
||||
new_address.save()
|
||||
messages.success(request, "Vendor address saved")
|
||||
#add email notification
|
||||
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
|
||||
return render(request, 'base/vendorAddr.html', context)
|
||||
except Exception as e:
|
||||
messages.error(request, e) #
|
||||
else:
|
||||
messages.error(request, "Something wrong happened, try again!")
|
||||
except Exception as e:
|
||||
messages.error(request, "Something wrong happened, try again!")
|
||||
|
||||
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
|
||||
return render(request, 'base/vendorAddr.html', context)
|
||||
except Exception as e:
|
||||
messages.error(request, "Something wrong happened, try again!")
|
||||
return redirect('vendor')
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-z#_ppfgs06)e4v18t!970-&-&jkxht!tw&ms#u5n_m-fbxulwa
|
|||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = ['10.0.70.5', '127.0.0.1']
|
||||
|
||||
# User substitution
|
||||
# https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#auth-custom-user
|
||||
|
@ -43,6 +43,13 @@ INSTALLED_APPS = [
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_bcrypt',
|
||||
'django_otp',
|
||||
'django_otp.plugins.otp_static',
|
||||
'django_otp.plugins.otp_totp',
|
||||
'django_otp.plugins.otp_email', # <- if you want email capability.
|
||||
'two_factor',
|
||||
'two_factor.plugins.phonenumber', # <- if you want phone number capability.
|
||||
'two_factor.plugins.email', # <- if you want email capability.
|
||||
#
|
||||
'base.apps.BaseConfig',
|
||||
'users.apps.UsersConfig',
|
||||
|
@ -54,6 +61,7 @@ MIDDLEWARE = [
|
|||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django_otp.middleware.OTPMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
@ -113,13 +121,13 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
'''
|
||||
PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
||||
]
|
||||
|
||||
'''
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
|
@ -143,23 +151,44 @@ STATICFILES_DIRS = [
|
|||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'handlers': {
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'two_factor': {
|
||||
'handlers': ['console'],
|
||||
'level': 'INFO',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Messages customize
|
||||
|
||||
MESSAGE_TAGS = {
|
||||
messages.DEBUG: "alert-info",
|
||||
messages.INFO: "alert-info",
|
||||
messages.SUCCESS: "alert-success",
|
||||
messages.WARNING: "alert-warning",
|
||||
messages.ERROR: "alert-danger",
|
||||
messages.DEBUG: "text-info",
|
||||
messages.INFO: "text-info",
|
||||
messages.SUCCESS: "text-success",
|
||||
messages.WARNING: "text-warning",
|
||||
messages.ERROR: "text-danger",
|
||||
}
|
||||
|
||||
BCRYPT_ROUNDS = 11
|
||||
|
||||
PASSWORD_RESET_TIMEOUT = 3600 #
|
||||
#LOGIN_URL = 'two_factor:login'
|
||||
#OTP_LOGIN_URL = 'account:login'
|
||||
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||
SESSION_COOKIE_SECURE = False #to be switched to true when in prod
|
||||
CSRF_COOKIE_SECURE = False #to be switched to true when in prod
|
||||
SECURE_SSL_REDIRECT = False #to be switched to true when in prod
|
||||
SESSION_COOKIE_AGE = 3600
|
||||
SERVER_EMAIL = 'info@litepay.ch'
|
||||
TWO_FACTOR_REMEMBER_COOKIE_AGE = 3600
|
||||
SERVER_EMAIL = 'info@litepay.ch'
|
||||
DEFAULT_FROM_EMAIL = 'info@litepay.ch'
|
|
@ -16,9 +16,15 @@ Including another URLconf
|
|||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from two_factor.urls import urlpatterns as tf_urls
|
||||
urlpatterns = [
|
||||
#path('', include(tf_urls)),
|
||||
path('', include('base.urls'))
|
||||
]
|
||||
|
||||
|
||||
'''
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('base.urls'))
|
||||
|
||||
]
|
||||
'''
|
Binary file not shown.
|
@ -18,9 +18,7 @@
|
|||
</div>
|
||||
<!-- End Page-content -->
|
||||
|
||||
{% block footer %}
|
||||
{% include "partials/footer.html" %}
|
||||
{% endblock footer %}
|
||||
|
||||
</div>
|
||||
<!-- end main content-->
|
||||
{% endblock content %}
|
|
@ -1,23 +1,27 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="light">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{% block title %}{% endblock title %} | </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta content="Premium Multipurpose Admin & Dashboard Template" name="description" />
|
||||
<meta content="Themesbrand" name="author" />
|
||||
<!-- App favicon -->
|
||||
<link rel="shortcut icon" href="{% static 'images/favicon.ico'%}">
|
||||
{% block css %}
|
||||
<!-- Bootstrap Css -->
|
||||
<link href="https://unpkg.com/@webpixels/css@1.1.5/dist/index.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
|
||||
<!-- Icons Css -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.4.0/font/bootstrap-icons.min.css" rel="stylesheet" type="text/css" />
|
||||
{% endblock css %}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="color-scheme" content="dark light">
|
||||
<title>{% block title %}{% endblock title %} |</title>
|
||||
<!-- App favicon -->
|
||||
<link rel="shortcut icon" href="{% static 'images/favicon.ico'%}">
|
||||
{% block css %}
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" type="text/css" href="{% static '/css/main.css'%}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static '/css/utility.css'%}">
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f=satoshi@900,700,500,300,401,400&display=swap">
|
||||
{% endblock css %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<body class="bg-surface-secondary">
|
||||
<!-- Begin page -->
|
||||
<div id="layout-wrapper">
|
||||
{% block header %}
|
||||
|
@ -29,8 +33,6 @@
|
|||
{% block content %}
|
||||
{% block pagetitle %}
|
||||
{% endblock pagetitle %}
|
||||
{% block footer %}
|
||||
{% endblock footer %}
|
||||
{% endblock content %}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
{% block footer %}
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="col-sm-6 text-center">
|
||||
<script>document.write(new Date().getFullYear())</script> © Litepay.ch.
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,90 +1,257 @@
|
|||
{% load static %}
|
||||
{% block sidebar %}
|
||||
<div class="d-flex flex-column flex-lg-row h-lg-full bg-surface-secondary">
|
||||
<!-- Vertical Navbar -->
|
||||
<nav class="navbar show navbar-vertical h-lg-screen navbar-expand-lg px-0 py-3 navbar-light bg-white border-bottom border-bottom-lg-0 border-end-lg" id="navbarVertical">
|
||||
<div class="container-fluid">
|
||||
<!-- Toggler -->
|
||||
<button class="navbar-toggler ms-n2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-controls="sidebarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<!-- Brand -->
|
||||
<a class="navbar-brand py-lg-2 mb-lg-5 px-lg-6 me-0" href="#">
|
||||
<img src="https://preview.webpixels.io/web/img/logos/clever-primary.svg" alt="...">
|
||||
</a>
|
||||
<!-- User menu (mobile) -->
|
||||
<div class="navbar-user d-lg-none">
|
||||
<!-- Dropdown -->
|
||||
<div class="dropdown">
|
||||
<!-- Toggle -->
|
||||
<a href="#" id="sidebarAvatar" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<div class="avatar-parent-child">
|
||||
<img alt="Image Placeholder" src="https://images.unsplash.com/photo-1548142813-c348350df52b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar- rounded-circle">
|
||||
<span class="avatar-child avatar-badge bg-success"></span>
|
||||
</div>
|
||||
</a>
|
||||
<!-- Menu -->
|
||||
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="sidebarAvatar">
|
||||
<a href="#" class="dropdown-item">Profile</a>
|
||||
<a href="#" class="dropdown-item">Settings</a>
|
||||
<a href="#" class="dropdown-item">Billing</a>
|
||||
<hr class="dropdown-divider">
|
||||
<a href="#" class="dropdown-item">Logout</a>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-lg-row h-lg-full">
|
||||
<nav class="flex-none navbar navbar-vertical navbar-expand-lg navbar-light bg-transparent show h-lg-screen px-0 py-2 scrollbar" id="sidebar">
|
||||
<div class="container-fluid px-3 px-md-4 px-lg-6">
|
||||
<button class="navbar-toggler ms-n2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-controls="sidebarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<a class="navbar-brand d-inline-block py-lg-1 mb-lg-5" href="/pages/dashboard.html">
|
||||
<img src="https://ik.imagekit.io/litepaych/assets/new/img/logo_litepay_2020_blue.png" class="h-8 h-md-10" alt="...">
|
||||
</a>
|
||||
<div class="navbar-user d-lg-none">
|
||||
<div class="dropdown">
|
||||
<a class="d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="false" aria-expanded="false">
|
||||
<div>
|
||||
<div class="avatar avatar-sm text-bg-secondary rounded-circle">
|
||||
AE
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-sm-block ms-3">
|
||||
<span class="h6">Alexis</span>
|
||||
</div>
|
||||
<div class="d-none d-md-block ms-md-2">
|
||||
<i class="bi bi-chevron-down text-muted text-xs"></i>
|
||||
</div>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<div class="dropdown-header">
|
||||
<span class="d-block text-sm text-muted mb-1">Signed in as</span>
|
||||
<span class="d-block text-heading font-semibold">Alexis Enache</span>
|
||||
</div>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="bi bi-house me-3"></i>Home
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="bi bi-pencil me-3"></i>Edit profile
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="bi bi-gear me-3"></i>Settings
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="bi bi-image me-3"></i>Media
|
||||
</a>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="bi bi-box-arrow-up me-3"></i>Share
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#">
|
||||
<i class="bi bi-person me-3"></i>Login
|
||||
</a>
|
||||
</div>
|
||||
<!-- Collapse -->
|
||||
<div class="collapse navbar-collapse" id="sidebarCollapse">
|
||||
<!-- Navigation -->
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<i class="bi bi-house"></i> Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<i class="bi bi-bar-chart"></i> Analitycs
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<i class="bi bi-chat"></i> Messages
|
||||
<span class="badge bg-soft-primary text-primary rounded-pill d-inline-flex align-items-center ms-auto">6</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">
|
||||
<i class="bi bi-bookmarks"></i> Collections
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'vendor' %}">
|
||||
<i class="bi bi-people"></i> Vendor
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Divider -->
|
||||
<hr class="navbar-divider my-5 opacity-20">
|
||||
<!-- Navigation -->
|
||||
|
||||
<!-- Push content down
|
||||
<div class="mt-auto"></div> -->
|
||||
<!-- User (md) -->
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'login' %}">
|
||||
<i class="bi bi-person-square"></i> Account
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'logout' %}">
|
||||
<i class="bi bi-box-arrow-left"></i> Logout
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse overflow-x-hidden" id="sidebarCollapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="/">
|
||||
<i class="bi bi-houses-fill"></i>
|
||||
<span>Dashboard</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="/pages/analytics.html">
|
||||
<i class="bi bi-pie-chart-fill"></i>
|
||||
<span>Analytics</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="/pages/wallet.html">
|
||||
<i class="bi bi-wallet-fill"></i>
|
||||
<span>Wallet</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="{% url 'vendor' %}">
|
||||
<i class="bi bi-wallet-fill"></i>
|
||||
<span>Vendor</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-exchange" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-exchange">
|
||||
<i class="bi bi-bar-chart-fill"></i>
|
||||
<span>Exchange</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
<div class="collapse" id="sidebar-exchange">
|
||||
<ul class="nav nav-sm flex-column mt-1">
|
||||
<li class="nav-item">
|
||||
<a href="/pages/exchange-watchlist.html" class="nav-link">
|
||||
Watchlist
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/exchange-history.html" class="nav-link">
|
||||
Trade History
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/exchange-details.html" class="nav-link">
|
||||
Trade Details
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/exchange-create-form.html" class="nav-link">
|
||||
Create Asset
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/exchange-staking.html" class="nav-link">
|
||||
Staking
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-account" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-account">
|
||||
<i class="bi bi-gear-fill"></i>
|
||||
<span>Account</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
<div class="collapse" id="sidebar-account">
|
||||
<ul class="nav nav-sm flex-column mt-1">
|
||||
<li class="nav-item">
|
||||
<a href="/pages/account-general.html" class="nav-link">
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/account-password.html" class="nav-link">
|
||||
Password
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/account-billing.html" class="nav-link">
|
||||
Billing
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/account-notifications.html" class="nav-link">
|
||||
Notifications
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/login.html" class="nav-link">
|
||||
Login
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/register.html" class="nav-link">
|
||||
Register
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-pages" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-pages">
|
||||
<i class="bi bi-file-break-fill"></i>
|
||||
<span>Pages</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
<div class="collapse" id="sidebar-pages">
|
||||
<ul class="nav nav-sm flex-column mt-1">
|
||||
<li class="nav-item">
|
||||
<a href="/pages/other-pricing.html" class="nav-link">
|
||||
Pricing Plans
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/terms.html" class="nav-link">
|
||||
Terms of Service
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/pages/error.html" class="nav-link">
|
||||
Error Page
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/index.html" class="nav-link">
|
||||
Landing Page
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Divider -->
|
||||
<hr class="navbar-divider my-5 opacity-70">
|
||||
<ul class="navbar-nav">
|
||||
<li>
|
||||
<span class="nav-link text-xs font-semibold text-uppercase text-muted ls-wide">
|
||||
Resources
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="{% url 'logout' %}">
|
||||
<i class="bi bi-door-closed-fill"></i>
|
||||
<span>Logout</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item my-1">
|
||||
<a class="nav-link d-flex align-items-center rounded-pill" href="https://themes.getbootstrap.com/product/satoshi-defi-and-crypto-exchange-theme">
|
||||
<i class="bi bi-journal-album"></i>
|
||||
<span>Changelog</span>
|
||||
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto">v1.0</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mt-auto"></div>
|
||||
<div class="card bg-dark border-0 mt-5 mb-3">
|
||||
<div class="card-body">
|
||||
<div class="vstack gap-4">
|
||||
<i class="bi bi-emoji-sunglasses-fill text-primary text-xl"></i>
|
||||
<p class="text-sm text-white text-opacity-70">
|
||||
Upgrade your account to Pro for even more examples.
|
||||
</p>
|
||||
<a href="/pages/pricing.html" class="btn btn-sm btn-primary w-full rounded-pill">
|
||||
Upgade now<i class="bi bi-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="flex-lg-fill overflow-x-auto vstack h-lg-screen">
|
||||
<div class="d-none d-lg-flex py-4">
|
||||
<div class="flex-none">
|
||||
|
||||
</div>
|
||||
<div class=" d-lg-none d-xxl-flex align-items-center gap-4 px-4 scrollable-x">
|
||||
<div class="d-flex gap-2 text-xs">
|
||||
<span class="text-heading font-semibold">Cryptos:</span>
|
||||
<span class="text-muted">21,713</span>
|
||||
</div>
|
||||
<div class="d-flex gap-2 text-xs">
|
||||
<span class="text-heading font-semibold">Market Cap:</span>
|
||||
<span class="text-muted">$871,322,862,585</span>
|
||||
</div>
|
||||
<div class="d-flex gap-2 text-xs">
|
||||
<span class="text-heading font-semibold">24h Vol:</span>
|
||||
<span class="text-muted">$180,639,667,232</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock sidebar %}
|
||||
|
|
@ -1,7 +1,15 @@
|
|||
{% load static %}
|
||||
{% block header %}
|
||||
<!-- Banner -->
|
||||
<a href="#" class="btn w-full btn-primary text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<a href="#"{% if message.tags %} class="btn {{ message.tags }} w-full text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;" {% endif %}>{{ message }}</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<!--
|
||||
<a href="#" class="btn btn-danger w-full text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;">
|
||||
<strong>Strong</strong> message here→
|
||||
</a>
|
||||
-->
|
||||
|
||||
{% endblock header %}
|
|
@ -38,6 +38,7 @@ class User(AbstractUser):
|
|||
username = None
|
||||
email = models.EmailField(_('email address'), unique=True)
|
||||
percentage = models.DecimalField(decimal_places=3, default=0.010, max_digits=4)
|
||||
vendornr = models.IntegerField(default=9)
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
|
|
Loading…
Reference in New Issue