Skip to content

Commit

Permalink
Fix currency
Browse files Browse the repository at this point in the history
  • Loading branch information
PleatherStarfish committed Dec 20, 2024
1 parent 7446e8c commit 4c76562
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 39 deletions.
3 changes: 3 additions & 0 deletions backend/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,9 @@ def patch(self, request):
"""
Update the user's default currency and fetch the new exchange rate.
"""
print("PATCH")
new_currency = request.data.get("default_currency")
print(new_currency)

# Validate the currency
if not any(new_currency == code for code, _ in CustomUser.CURRENCIES):
Expand All @@ -376,6 +378,7 @@ def patch(self, request):
try:
# Retrieve the exchange rate using the provided utility function
exchange_rate_to_usd = get_exchange_rate(new_currency)
print(exchange_rate_to_usd)
except ValueError as e:
return Response(
{"error": str(e)},
Expand Down
36 changes: 21 additions & 15 deletions backend/core/openexchangerates.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import os
import requests
import sentry_sdk
from sentry_sdk import capture_exception


class OpenExchangeRatesError(Exception):
"""Custom exception for Open Exchange Rates errors."""

print("OpenExchangeRatesError")
# Initialize Sentry SDK (ensure this is done at the application startup)
sentry_sdk.init(
dsn=os.getenv("SENTRY_DSN"), # Replace with your Sentry DSN
traces_sample_rate=1.0, # Adjust this based on your needs
)


def get_latest_exchange_rates(base_currency="USD"):
api_key = os.getenv("OPENEXCHANGERATES_APP_ID")
if not api_key:
raise OpenExchangeRatesError(
"OPENEXCHANGERATES_APP_ID is not set in the environment"
)
error_message = "OPENEXCHANGERATES_APP_ID is not set in the environment"
capture_exception(Exception(error_message))
return {"error": error_message}

url = f"https://openexchangerates.org/api/latest.json?app_id={api_key}&base={base_currency}"
print(url)
response = requests.get(url)
print(response)
if response.status_code != 200:
raise OpenExchangeRatesError(
f"Error fetching exchange rates: {response.json().get('error', 'Unknown error')}"
)
try:
response = requests.get(url)
print(response)
if response.status_code != 200:
error_message = f"Error fetching exchange rates: {response.json().get('error', 'Unknown error')}"
capture_exception(Exception(error_message))
return {"error": error_message}

return response.json()
return response.json()
except Exception as e:
capture_exception(e)
return {"error": str(e)}
11 changes: 8 additions & 3 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


def get_exchange_rate(target_currency: str) -> float:
print("TEST")
print(target_currency)
"""
Retrieve the exchange rate for the given target currency against USD.
Expand All @@ -35,11 +35,14 @@ def get_exchange_rate(target_currency: str) -> float:
if not target_currency or len(target_currency) != 3:
raise ValueError(f"Invalid target currency: {target_currency}")

# If the target currency is USD, the exchange rate is always 1.0
if target_currency == "USD":
return 1.0

# Try to retrieve from the database
exchange_rate = ExchangeRate.objects.filter(
base_currency="USD", target_currency=target_currency
).first()
print(exchange_rate)

if exchange_rate and exchange_rate.last_updated > now() - timedelta(hours=24):
# Return the cached rate if it's fresh
Expand All @@ -55,12 +58,14 @@ def get_exchange_rate(target_currency: str) -> float:
if rate is None:
raise ValueError(f"Exchange rate not available for USD to {target_currency}")

# Update or create the exchange rate record
# Update the record if it exists, or create a new one
if exchange_rate:
print("UPDATE")
exchange_rate.rate = rate
exchange_rate.last_updated = now()
exchange_rate.save()
else:
print("CREATE")
ExchangeRate.objects.create(
base_currency="USD",
target_currency=target_currency,
Expand Down
2 changes: 1 addition & 1 deletion backend/static/css/styles.css

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions backend/static/css/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
top: -200px;
width: 180%;
}

.widget__actor {
color: #ebe6ef !important;
}

@media (min-width: 1040px) {
#parallax-image {
Expand Down
8 changes: 3 additions & 5 deletions backend/static/js/currency_selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ function updateCurrency(newCurrency) {
const currencyContainer = $("#currency-container");
const isAuthenticated = currencyContainer.data("is-authenticated") === "true";
const csrfToken = currencyContainer.data("csrf-token");
const userCurrencyUrl = currencyContainer.data("url-user-currency");
console.log("Fetching URL:", userCurrencyUrl);
console.log("CSRF Token:", csrfToken);
console.log("Currency to update:", newCurrency);
console.log(isAuthenticated)
console.log(csrfToken)

// Update user's default currency on the server if authenticated
if (isAuthenticated) {
fetch(userCurrencyUrl, {
fetch("/api/user-update-currency/", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Expand Down
4 changes: 2 additions & 2 deletions backend/static/js/main.bundle.js

Large diffs are not rendered by default.

100 changes: 98 additions & 2 deletions backend/templates/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
crossorigin="anonymous"></script>
<link
rel="preload"
rel="stylesheet"
as="style"
href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"
/>
<!-- Include Choices.js JavaScript from CDN -->
Expand Down Expand Up @@ -128,9 +128,105 @@ <h3 class="text-lg font-medium leading-6 text-white">Cookie Consent</h3>
<script src="{% static 'js/mobile_menu.js' %}" defer></script>
<script src="{% static 'js/logo_shrink.js' %}" defer></script>
<script src="{% static 'js/hide_toast_notifications.js' %}" defer></script>
<script src="{% static 'js/currency_selector.js' %}" defer></script>
<script src="{% static 'js/banner.js' %}" defer></script>
<script src="{% static 'js/mobile_dropdown_toggle.js' %}" defer></script>

<script>
function updateCurrency(newCurrency) {
localStorage.setItem("currency", newCurrency);

const isAuthenticated = {{ request.user.is_authenticated|yesno:"true,false" }};

// Update user's default currency on the server if authenticated
if (isAuthenticated) {
fetch("{% url 'user_currency' %}", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": "{{ csrf_token|escapejs }}", // Include CSRF token safely
},
body: JSON.stringify({ default_currency: newCurrency }),
})
.then((response) => {
console.log("Response status:", response.status); // Debug response status
if (response.ok || response.status === 403) {
console.log("Reloading page...");
window.location.replace(window.location.href); // Force reload
} else {
console.error("Error updating currency:", response.statusText);
}
})
.catch((error) => {
console.error("Network error:", error);
window.location.replace(window.location.href); // Reload on error
});
} else {
console.log("User not authenticated, reloading...");
window.location.replace(window.location.href); // Reload for unauthenticated users
}
}

$(document).ready(function () {
const currencies = [
{ code: "USD", symbol: "$", name: "US Dollar" },
{ code: "EUR", symbol: "€", name: "Euro" },
{ code: "JPY", symbol: "¥", name: "Japanese Yen" },
{ code: "GBP", symbol: "£", name: "British Pound" },
{ code: "AUD", symbol: "A$", name: "Australian Dollar" },
{ code: "CAD", symbol: "C$", name: "Canadian Dollar" },
{ code: "CHF", symbol: "CHF", name: "Swiss Franc" },
{ code: "CNY", symbol: "¥", name: "Chinese Yuan" },
{ code: "HKD", symbol: "HK$", name: "Hong Kong Dollar" },
{ code: "NZD", symbol: "NZ$", name: "New Zealand Dollar" },
{ code: "SEK", symbol: "kr", name: "Swedish Krona" },
{ code: "KRW", symbol: "₩", name: "South Korean Won" },
{ code: "SGD", symbol: "S$", name: "Singapore Dollar" },
{ code: "NOK", symbol: "kr", name: "Norwegian Krone" },
{ code: "INR", symbol: "₹", name: "Indian Rupee" },
];

const currencySelector = $("#currency-selector");

function getDefaultCurrency() {
const userDefaultCurrency = "{{ request.user.default_currency|escapejs|default:'' }}";
const isAuthenticated = {{ request.user.is_authenticated|yesno:"true,false" }};
if (isAuthenticated && userDefaultCurrency) {
return userDefaultCurrency;
}
return localStorage.getItem("currency") || "USD";
}

function populateWithSymbols() {
currencySelector.empty();
currencies.forEach((currency) => {
const option = $("<option>")
.val(currency.code)
.text(currency.symbol);
currencySelector.append(option);
});
currencySelector.val(getDefaultCurrency());
}

function populateWithFullNames() {
currencySelector.empty();
currencies.forEach((currency) => {
const option = $("<option>")
.val(currency.code)
.text(`${currency.name} (${currency.code})`);
currencySelector.append(option);
});
currencySelector.val(getDefaultCurrency());
}

currencySelector.on("focus", populateWithFullNames);
currencySelector.on("blur", populateWithSymbols);
populateWithSymbols();

currencySelector.on("change", function () {
updateCurrency(this.value);
});
});
</script>

{% block javascript %}{% endblock javascript %}
</body>
Expand Down
36 changes: 26 additions & 10 deletions frontend/src/components/bom_list/checkboxGridModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
output[supplier] = items
.map((item) => `${item?.itemNumber}|${item?.quantity}`)
.join("\n");
} else if (["DigiKey", "TME"].includes(supplier)) {
} else if (["DigiKey", "TME", "Tayda"].includes(supplier)) {
output[supplier] = items
.map((item) => `${item?.itemNumber},${item?.quantity}`)
.join("\n");
Expand Down Expand Up @@ -164,7 +164,7 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
<>
{/* Supplier and Components Table */}
<div className="overflow-x-auto">
<table className="min-w-full border-collapse border border-gray-200">
<table className="min-w-full border border-collapse border-gray-200">
<thead>
<tr>
<th className="px-4 py-2 text-left border border-gray-200">
Expand Down Expand Up @@ -239,7 +239,7 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
<LinkIcon className="inline-block w-3 h-3" />
</a>
)}
<span className="text-2xs text-gray-600">
<span className="text-gray-600 text-2xs">
{item?.price
? `($${item?.price})`
: ""}
Expand All @@ -261,11 +261,11 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
<div id="supplier-import-tool">
{Object.entries(formattedOutput).map(([supplier, output]) => (
<div className="p-4" key={supplier}>
<h3 className="text-lg font-semibold text-gray-800 mb-2 text-left">
<h3 className="mb-2 text-lg font-semibold text-left text-gray-800">
{supplier}
</h3>
{supplier === "Mouser" && (
<p className="text-sm text-gray-600 mb-4 text-left">
<p className="mb-4 text-sm text-left text-gray-600">
To generate a Mouser cart, copy the text below, and paste it
into the{" "}
<a
Expand All @@ -280,7 +280,7 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
</p>
)}
{supplier === "DigiKey" && (
<p className="text-sm text-gray-600 mb-4 text-left">
<p className="mb-4 text-sm text-left text-gray-600">
To generate a DigiKey cart, copy the text below, and paste
it into the{" "}
<a
Expand All @@ -295,7 +295,7 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
</p>
)}
{supplier === "TME" && (
<p className="text-sm text-gray-600 mb-4">
<p className="mb-4 text-sm text-gray-600">
To generate a TME cart, copy the text below, and paste it
into the{" "}
<a
Expand All @@ -311,9 +311,25 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
space. Each product must be listed on a separate line.
</p>
)}
<div className="relative hover:bg-stone-200 bg-stone-100 cursor-copy p-4 rounded w-full text-left">
{supplier === "Tayda" && (
<p className="mb-4 text-sm text-gray-600">
To generate a Tayda cart, copy the text below, and paste it
into the{" "}
<a
className="text-blue-500 hover:underline"
href="https://www.taydaelectronics.com/quick-order/"
rel="noreferrer"
target="_blank"
>
Tayda Quick Order tool
</a>
. Ensure each line contains a product’s symbol followed by
its quantity, separated by a comma. Each product must be listed on a separate line.
</p>
)}
<div className="relative w-full p-4 text-left rounded hover:bg-stone-200 bg-stone-100 cursor-copy">
<pre
className="whitespace-pre-wrap text-sm text-gray-800"
className="text-sm text-gray-800 whitespace-pre-wrap"
onClick={() => {
navigator.clipboard.writeText(output).then(() => {
alert("Copied to clipboard!");
Expand All @@ -323,7 +339,7 @@ const CheckboxGridModal: React.FC<CheckboxGridModalProps> = ({
{output}
</pre>
<ClipboardDocumentListIcon
className="absolute top-2 right-2 w-6 h-6 text-gray-600 cursor-pointer hover:text-gray-800"
className="absolute w-6 h-6 text-gray-600 cursor-pointer top-2 right-2 hover:text-gray-800"
onClick={() => {
navigator.clipboard.writeText(output).then(() => {
alert("Copied to clipboard!");
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/pages/ModuleDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,11 @@ const ModuleDetail: React.FC = () => {
</a>{" "}
to compare the BOM against your personal inventory.
</span>)}
<div>
<p className="text-gray-500">Click any row in the BOM to see a list of components that fulfill that component requirement.</p>
</div>
</div>

<div className="pb-6">
<BomList
bomUnderConstruction={!!module.bom_under_construction}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/styles/styles.css

Large diffs are not rendered by default.

0 comments on commit 4c76562

Please sign in to comment.